﻿//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  License: GNU Lesser General Public License (LGPLv3)
//
//  Email: pavel_torgashov@ukr.net.
//
//  Copyright (C) Pavel Torgashov, 2011-2014. 

//#define debug

// -------------------------------------------------------------------------------
// By default the FastColoredTextbox supports no more 16 styles at the same time.
// This restriction saves memory.
// However, you can to compile FCTB with 32 styles supporting.
// Uncomment following definition if you need 32 styles instead of 16:
//
// #define Styles32

namespace FastColoredTextBoxNS
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Drawing.Drawing2D;
    using System.IO;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;

    using Microsoft.Win32;

    using Timer = System.Windows.Forms.Timer;

    /// <summary>
    /// 	Fast colored textbox
    /// </summary>
    public class FastColoredTextBox : UserControl, ISupportInitialize
    {
        internal const int minLeftIndent = 8;

        private const int maxBracketSearchIterations = 1000;

        private const int maxLinesForFolding = 3000;

        private const int minLinesForAccuracy = 100000;

        private const int WM_IME_SETCONTEXT = 0x0281;

        private const int WM_HSCROLL = 0x114;

        private const int WM_VSCROLL = 0x115;

        private const int SB_ENDSCROLL = 0x8;

        public readonly List<LineInfo> LineInfos = new List<LineInfo>();

        private readonly Range selection;

        private readonly Timer timer = new Timer();

        private readonly Timer timer2 = new Timer();

        private readonly Timer timer3 = new Timer();

        private readonly List<VisualMarker> visibleMarkers = new List<VisualMarker>();

        public int TextHeight;

        public bool AllowInsertRemoveLines = true;

        private Brush backBrush;

        private BaseBookmarks bookmarks;

        private bool caretVisible;

        private Color changedLineColor;

        private int charHeight;

        private Color currentLineColor;

        private Cursor defaultCursor;

        private Range delayedTextChangedRange;

        private string descriptionFile;

        private int endFoldingLine = -1;

        private Color foldingIndicatorColor;

        protected Dictionary<int, int> foldingPairs = new Dictionary<int, int>();

        private bool handledChar;

        private bool highlightFoldingIndicator;

        private Hints hints;

        private Color indentBackColor;

        private bool isChanged;

        private bool isLineSelect;

        private bool isReplaceMode;

        private Language language;

        private Keys lastModifiers;

        private Point lastMouseCoord;

        private DateTime lastNavigatedDateTime;

        private Range leftBracketPosition;

        private Range leftBracketPosition2;

        private int leftPadding;

        private int lineInterval;

        private Color lineNumberColor;

        private uint lineNumberStartValue;

        private int lineSelectFrom;

        private TextSource lines;

        private IntPtr m_hImc;

        private int maxLineLength;

        private bool mouseIsDrag;

        private bool mouseIsDragDrop;

        private bool multiline;

        protected bool needRecalc;

        protected bool needRecalcWordWrap;

        private Point needRecalcWordWrapInterval;

        private bool needRecalcFoldingLines;

        private bool needRiseSelectionChangedDelayed;

        private bool needRiseTextChangedDelayed;

        private bool needRiseVisibleRangeChangedDelayed;

        private Color paddingBackColor;

        private int preferredLineWidth;

        private Range rightBracketPosition;

        private Range rightBracketPosition2;

        private bool scrollBars;

        private Color selectionColor;

        private Color serviceLinesColor;

        private bool showFoldingLines;

        private bool showLineNumbers;

        private FastColoredTextBox sourceTextBox;

        private int startFoldingLine = -1;

        private int updating;

        private Range updatingRange;

        private Range visibleRange;

        private bool wordWrap;

        private WordWrapMode wordWrapMode = WordWrapMode.WordWrapControlWidth;

        private int reservedCountOfLineNumberChars = 1;

        private int zoom = 100;

        private Size localAutoScrollMinSize;

        /// <summary>
        /// 	Constructor
        /// </summary>
        public FastColoredTextBox()
        {
            //register type provider
            TypeDescriptionProvider prov = TypeDescriptor.GetProvider(this.GetType());
            object theProvider =
                prov.GetType().GetField("Provider", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(prov);
            if (theProvider.GetType() != typeof(FCTBDescriptionProvider))
            {
                TypeDescriptor.AddProvider(new FCTBDescriptionProvider(this.GetType()), this.GetType());
            }
            //drawing optimization
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            //append monospace font
            //Font = new Font("Consolas", 9.75f, FontStyle.Regular, GraphicsUnit.Point);
            this.Font = new Font(FontFamily.GenericMonospace, 9.75f);
            //create one line
            this.InitTextSource(this.CreateTextSource());
            if (this.lines.Count == 0)
            {
                this.lines.InsertLine(0, this.lines.CreateLine());
            }
            this.selection = new Range(this) { Start = new Place(0, 0) };
            //default settings
            this.Cursor = Cursors.IBeam;
            this.BackColor = Color.White;
            this.LineNumberColor = Color.Teal;
            this.IndentBackColor = Color.WhiteSmoke;
            this.ServiceLinesColor = Color.Silver;
            this.FoldingIndicatorColor = Color.Green;
            this.CurrentLineColor = Color.Transparent;
            this.ChangedLineColor = Color.Transparent;
            this.HighlightFoldingIndicator = true;
            this.ShowLineNumbers = true;
            this.TabLength = 4;
            this.FoldedBlockStyle = new FoldedBlockStyle(Brushes.Gray, null, FontStyle.Regular);
            this.SelectionColor = Color.Blue;
            this.BracketsStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(80, Color.Lime)));
            this.BracketsStyle2 = new MarkerStyle(new SolidBrush(Color.FromArgb(60, Color.Red)));
            this.DelayedEventsInterval = 100;
            this.DelayedTextChangedInterval = 100;
            this.AllowSeveralTextStyleDrawing = false;
            this.LeftBracket = '\x0';
            this.RightBracket = '\x0';
            this.LeftBracket2 = '\x0';
            this.RightBracket2 = '\x0';
            this.SyntaxHighlighter = new SyntaxHighlighter();
            this.language = Language.Custom;
            this.PreferredLineWidth = 0;
            this.needRecalc = true;
            this.lastNavigatedDateTime = DateTime.Now;
            this.AutoIndent = true;
            this.AutoIndentExistingLines = true;
            this.CommentPrefix = "//";
            this.lineNumberStartValue = 1;
            this.multiline = true;
            this.scrollBars = true;
            this.AcceptsTab = true;
            this.AcceptsReturn = true;
            this.caretVisible = true;
            this.CaretColor = Color.Black;
            this.WideCaret = false;
            this.Paddings = new Padding(0, 0, 0, 0);
            this.PaddingBackColor = Color.Transparent;
            this.DisabledColor = Color.FromArgb(100, 180, 180, 180);
            this.needRecalcFoldingLines = true;
            this.AllowDrop = true;
            this.FindEndOfFoldingBlockStrategy = FindEndOfFoldingBlockStrategy.Strategy1;
            this.VirtualSpace = false;
            this.bookmarks = new Bookmarks(this);
            this.BookmarkColor = Color.PowderBlue;
            this.ToolTip = new ToolTip();
            this.timer3.Interval = 500;
            this.hints = new Hints(this);
            this.SelectionHighlightingForLineBreaksEnabled = true;
            this.textAreaBorder = TextAreaBorderType.None;
            this.textAreaBorderColor = Color.Black;
            this.macrosManager = new MacrosManager(this);
            this.HotkeysMapping = new HotkeysMapping();
            this.HotkeysMapping.InitDefault();
            this.WordWrapAutoIndent = true;
            this.FoldedBlocks = new Dictionary<int, int>();
            this.AutoCompleteBrackets = false;
            this.AutoIndentCharsPatterns = @"^\s*[\w\.]+\s*(?<range>=)\s*(?<range>[^;]+);";
            this.AutoIndentChars = true;
            //
            base.AutoScroll = true;
            this.timer.Tick += this.timer_Tick;
            this.timer2.Tick += this.timer2_Tick;
            this.timer3.Tick += this.timer3_Tick;
            this.middleClickScrollingTimer.Tick += this.middleClickScrollingTimer_Tick;
        }

        private char[] autoCompleteBracketsList = { '(', ')', '{', '}', '[', ']', '"', '"', '\'', '\'' };

        public char[] AutoCompleteBracketsList
        {
            get
            {
                return this.autoCompleteBracketsList;
            }
            set
            {
                this.autoCompleteBracketsList = value;
            }
        }

        /// <summary>
        /// 	AutoComplete brackets
        /// </summary>
        [DefaultValue(false)]
        [Description("AutoComplete brackets.")]
        public bool AutoCompleteBrackets { get; set; }

        /// <summary>
        /// 	Contains UniqueId of start lines of folded blocks
        /// </summary>
        /// <remarks>
        /// 	This dictionary remembers folding state of blocks.
        /// 	It is needed to restore child folding after user collapsed/expanded top-level folding block.
        /// </remarks>
        [Browsable(false)]
        public Dictionary<int, int> FoldedBlocks { get; private set; }

        /// <summary>
        /// 	Strategy of search of brackets to highlighting
        /// </summary>
        [DefaultValue(typeof(BracketsHighlightStrategy), "Strategy1")]
        [Description("Strategy of search of brackets to highlighting.")]
        public BracketsHighlightStrategy BracketsHighlightStrategy { get; set; }

        /// <summary>
        /// 	Automatically shifts secondary wordwrap lines on the shift amount of the first line
        /// </summary>
        [DefaultValue(true)]
        [Description("Automatically shifts secondary wordwrap lines on the shift amount of the first line.")]
        public bool WordWrapAutoIndent { get; set; }

        /// <summary>
        /// 	Indent of secondary wordwrap lines (in chars)
        /// </summary>
        [DefaultValue(0)]
        [Description("Indent of secondary wordwrap lines (in chars).")]
        public int WordWrapIndent { get; set; }

        private readonly MacrosManager macrosManager;

        /// <summary>
        /// 	MacrosManager records, stores and executes the macroses
        /// </summary>
        [Browsable(false)]
        public MacrosManager MacrosManager
        {
            get
            {
                return this.macrosManager;
            }
        }

        /// <summary>
        /// 	Allows drag and drop
        /// </summary>
        [DefaultValue(true)]
        [Description("Allows drag and drop")]
        public override bool AllowDrop
        {
            get
            {
                return base.AllowDrop;
            }
            set
            {
                base.AllowDrop = value;
            }
        }

        /// <summary>
        /// 	Collection of Hints.
        /// 	This is temporary buffer for currently displayed hints.
        /// </summary>
        /// <remarks>
        /// 	You can asynchronously add, remove and clear hints. Appropriate hints will be shown or hidden from the screen.
        /// </remarks>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public Hints Hints
        {
            get
            {
                return this.hints;
            }
            set
            {
                this.hints = value;
            }
        }

        /// <summary>
        /// 	Delay (ms) of ToolTip
        /// </summary>
        [Browsable(true)]
        [DefaultValue(500)]
        [Description("Delay(ms) of ToolTip.")]
        public int ToolTipDelay
        {
            get
            {
                return this.timer3.Interval;
            }
            set
            {
                this.timer3.Interval = value;
            }
        }

        /// <summary>
        /// 	ToolTip component
        /// </summary>
        [Browsable(true)]
        [Description("ToolTip component.")]
        public ToolTip ToolTip { get; set; }

        /// <summary>
        /// 	Color of bookmarks
        /// </summary>
        [Browsable(true)]
        [DefaultValue(typeof(Color), "PowderBlue")]
        [Description("Color of bookmarks.")]
        public Color BookmarkColor { get; set; }

        /// <summary>
        /// 	Bookmarks
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public BaseBookmarks Bookmarks
        {
            get
            {
                return this.bookmarks;
            }
            set
            {
                this.bookmarks = value;
            }
        }

        /// <summary>
        /// 	Enables virtual spaces
        /// </summary>
        [DefaultValue(false)]
        [Description("Enables virtual spaces.")]
        public bool VirtualSpace { get; set; }

        /// <summary>
        /// 	Strategy of search of end of folding block
        /// </summary>
        [DefaultValue(FindEndOfFoldingBlockStrategy.Strategy1)]
        [Description("Strategy of search of end of folding block.")]
        public FindEndOfFoldingBlockStrategy FindEndOfFoldingBlockStrategy { get; set; }

        /// <summary>
        /// 	Indicates if tab characters are accepted as input
        /// </summary>
        [DefaultValue(true)]
        [Description("Indicates if tab characters are accepted as input.")]
        public bool AcceptsTab { get; set; }

        /// <summary>
        /// 	Indicates if return characters are accepted as input
        /// </summary>
        [DefaultValue(true)]
        [Description("Indicates if return characters are accepted as input.")]
        public bool AcceptsReturn { get; set; }

        /// <summary>
        /// 	Shows or hides the caret
        /// </summary>
        [DefaultValue(true)]
        [Description("Shows or hides the caret")]
        public bool CaretVisible
        {
            get
            {
                return this.caretVisible;
            }
            set
            {
                this.caretVisible = value;
                this.Invalidate();
            }
        }

        private Color textAreaBorderColor;

        /// <summary>
        /// 	Color of border of text area
        /// </summary>
        [DefaultValue(typeof(Color), "Black")]
        [Description("Color of border of text area")]
        public Color TextAreaBorderColor
        {
            get
            {
                return this.textAreaBorderColor;
            }
            set
            {
                this.textAreaBorderColor = value;
                this.Invalidate();
            }
        }

        private TextAreaBorderType textAreaBorder;

        /// <summary>
        /// 	Type of border of text area
        /// </summary>
        [DefaultValue(typeof(TextAreaBorderType), "None")]
        [Description("Type of border of text area")]
        public TextAreaBorderType TextAreaBorder
        {
            get
            {
                return this.textAreaBorder;
            }
            set
            {
                this.textAreaBorder = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Background color for current line
        /// </summary>
        [DefaultValue(typeof(Color), "Transparent")]
        [Description("Background color for current line. Set to Color.Transparent to hide current line highlighting")]
        public Color CurrentLineColor
        {
            get
            {
                return this.currentLineColor;
            }
            set
            {
                this.currentLineColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Background color for highlighting of changed lines
        /// </summary>
        [DefaultValue(typeof(Color), "Transparent")]
        [Description(
            "Background color for highlighting of changed lines. Set to Color.Transparent to hide changed line highlighting"
            )]
        public Color ChangedLineColor
        {
            get
            {
                return this.changedLineColor;
            }
            set
            {
                this.changedLineColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Fore color (default style color)
        /// </summary>
        public override Color ForeColor
        {
            get
            {
                return base.ForeColor;
            }
            set
            {
                base.ForeColor = value;
                this.lines.InitDefaultStyle();
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Height of char in pixels (includes LineInterval)
        /// </summary>
        [Browsable(false)]
        public int CharHeight
        {
            get
            {
                return this.charHeight;
            }
            set
            {
                this.charHeight = value;
                this.NeedRecalc();
                this.OnCharSizeChanged();
            }
        }

        /// <summary>
        /// 	Interval between lines (in pixels)
        /// </summary>
        [Description("Interval between lines in pixels")]
        [DefaultValue(0)]
        public int LineInterval
        {
            get
            {
                return this.lineInterval;
            }
            set
            {
                this.lineInterval = value;
                this.SetFont(this.Font);
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Width of char in pixels
        /// </summary>
        [Browsable(false)]
        public int CharWidth { get; set; }

        /// <summary>
        /// 	Spaces count for tab
        /// </summary>
        [DefaultValue(4)]
        [Description("Spaces count for tab")]
        public int TabLength { get; set; }

        /// <summary>
        /// 	Text was changed
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsChanged
        {
            get
            {
                return this.isChanged;
            }
            set
            {
                if (!value)
                {
                    //clear line's IsChanged property
                    this.lines.ClearIsChanged();
                }

                this.isChanged = value;
            }
        }

        /// <summary>
        /// 	Text version
        /// </summary>
        /// <remarks>
        /// 	This counter is incremented each time changes the text
        /// </remarks>
        [Browsable(false)]
        public int TextVersion { get; private set; }

        /// <summary>
        /// 	Read only
        /// </summary>
        [DefaultValue(false)]
        public bool ReadOnly { get; set; }

        /// <summary>
        /// 	Shows line numbers.
        /// </summary>
        [DefaultValue(true)]
        [Description("Shows line numbers.")]
        public bool ShowLineNumbers
        {
            get
            {
                return this.showLineNumbers;
            }
            set
            {
                this.showLineNumbers = value;
                this.NeedRecalc();
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Shows vertical lines between folding start line and folding end line.
        /// </summary>
        [DefaultValue(false)]
        [Description("Shows vertical lines between folding start line and folding end line.")]
        public bool ShowFoldingLines
        {
            get
            {
                return this.showFoldingLines;
            }
            set
            {
                this.showFoldingLines = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Rectangle where located text
        /// </summary>
        [Browsable(false)]
        public Rectangle TextAreaRect
        {
            get
            {
                int rightPaddingStartX = this.LeftIndent + this.maxLineLength * this.CharWidth + this.Paddings.Left + 1;
                rightPaddingStartX = Math.Max(this.ClientSize.Width - this.Paddings.Right, rightPaddingStartX);
                int bottomPaddingStartY = this.TextHeight + this.Paddings.Top;
                bottomPaddingStartY = Math.Max(this.ClientSize.Height - this.Paddings.Bottom, bottomPaddingStartY);
                int top = Math.Max(0, this.Paddings.Top - 1) - this.VerticalScroll.Value;
                int left = this.LeftIndent - this.HorizontalScroll.Value - 2 + Math.Max(0, this.Paddings.Left - 1);
                Rectangle rect = Rectangle.FromLTRB(
                    left,
                    top,
                    rightPaddingStartX - this.HorizontalScroll.Value,
                    bottomPaddingStartY - this.VerticalScroll.Value);
                return rect;
            }
        }

        /// <summary>
        /// 	Color of line numbers.
        /// </summary>
        [DefaultValue(typeof(Color), "Teal")]
        [Description("Color of line numbers.")]
        public Color LineNumberColor
        {
            get
            {
                return this.lineNumberColor;
            }
            set
            {
                this.lineNumberColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Start value of first line number.
        /// </summary>
        [DefaultValue(typeof(uint), "1")]
        [Description("Start value of first line number.")]
        public uint LineNumberStartValue
        {
            get
            {
                return this.lineNumberStartValue;
            }
            set
            {
                this.lineNumberStartValue = value;
                this.needRecalc = true;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Background color of indent area
        /// </summary>
        [DefaultValue(typeof(Color), "WhiteSmoke")]
        [Description("Background color of indent area")]
        public Color IndentBackColor
        {
            get
            {
                return this.indentBackColor;
            }
            set
            {
                this.indentBackColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Background color of padding area
        /// </summary>
        [DefaultValue(typeof(Color), "Transparent")]
        [Description("Background color of padding area")]
        public Color PaddingBackColor
        {
            get
            {
                return this.paddingBackColor;
            }
            set
            {
                this.paddingBackColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Color of disabled component
        /// </summary>
        [DefaultValue(typeof(Color), "100;180;180;180")]
        [Description("Color of disabled component")]
        public Color DisabledColor { get; set; }

        /// <summary>
        /// 	Color of caret
        /// </summary>
        [DefaultValue(typeof(Color), "Black")]
        [Description("Color of caret.")]
        public Color CaretColor { get; set; }

        /// <summary>
        /// 	Wide caret
        /// </summary>
        [DefaultValue(false)]
        [Description("Wide caret.")]
        public bool WideCaret { get; set; }

        /// <summary>
        /// 	Color of service lines (folding lines, borders of blocks etc.)
        /// </summary>
        [DefaultValue(typeof(Color), "Silver")]
        [Description("Color of service lines (folding lines, borders of blocks etc.)")]
        public Color ServiceLinesColor
        {
            get
            {
                return this.serviceLinesColor;
            }
            set
            {
                this.serviceLinesColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Padings of text area
        /// </summary>
        [Browsable(true)]
        [Description("Paddings of text area.")]
        public Padding Paddings { get; set; }

        //hide parent padding
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Padding Padding
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        //hide RTL
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new bool RightToLeft
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// 	Color of folding area indicator
        /// </summary>
        [DefaultValue(typeof(Color), "Green")]
        [Description("Color of folding area indicator.")]
        public Color FoldingIndicatorColor
        {
            get
            {
                return this.foldingIndicatorColor;
            }
            set
            {
                this.foldingIndicatorColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Enables folding indicator (left vertical line between folding bounds)
        /// </summary>
        [DefaultValue(true)]
        [Description("Enables folding indicator (left vertical line between folding bounds)")]
        public bool HighlightFoldingIndicator
        {
            get
            {
                return this.highlightFoldingIndicator;
            }
            set
            {
                this.highlightFoldingIndicator = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Left distance to text beginning
        /// </summary>
        [Browsable(false)]
        [Description("Left distance to text beginning.")]
        public int LeftIndent { get; private set; }

        /// <summary>
        /// 	Left padding in pixels
        /// </summary>
        [DefaultValue(0)]
        [Description("Width of left service area (in pixels)")]
        public int LeftPadding
        {
            get
            {
                return this.leftPadding;
            }
            set
            {
                this.leftPadding = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	This property draws vertical line after defined char position.
        /// 	Set to 0 for disable drawing of vertical line.
        /// </summary>
        [DefaultValue(0)]
        [Description(
            "This property draws vertical line after defined char position. Set to 0 for disable drawing of vertical line."
            )]
        public int PreferredLineWidth
        {
            get
            {
                return this.preferredLineWidth;
            }
            set
            {
                this.preferredLineWidth = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Styles
        /// </summary>
        [Browsable(false)]
        public Style[] Styles
        {
            get
            {
                return this.lines.Styles;
            }
        }

        /// <summary>
        /// 	Hotkeys. Do not use this property in your code, use HotkeysMapping property.
        /// </summary>
        [Description("Here you can change hotkeys for FastColoredTextBox.")]
        [Editor(typeof(HotkeysEditor), typeof(UITypeEditor))]
        [DefaultValue(
            "Tab=IndentIncrease, Escape=ClearHints, PgUp=GoPageUp, PgDn=GoPageDown, End=GoEnd, Home=GoHome, Left=GoLeft, Up=GoUp, Right=GoRight, Down=GoDown, Ins=ReplaceMode, Del=DeleteCharRight, F3=FindNext, Shift+Tab=IndentDecrease, Shift+PgUp=GoPageUpWithSelection, Shift+PgDn=GoPageDownWithSelection, Shift+End=GoEndWithSelection, Shift+Home=GoHomeWithSelection, Shift+Left=GoLeftWithSelection, Shift+Up=GoUpWithSelection, Shift+Right=GoRightWithSelection, Shift+Down=GoDownWithSelection, Shift+Ins=Paste, Shift+Del=Cut, Ctrl+Back=ClearWordLeft, Ctrl+Space=AutocompleteMenu, Ctrl+End=GoLastLine, Ctrl+Home=GoFirstLine, Ctrl+Left=GoWordLeft, Ctrl+Up=ScrollUp, Ctrl+Right=GoWordRight, Ctrl+Down=ScrollDown, Ctrl+Ins=Copy, Ctrl+Del=ClearWordRight, Ctrl+0=ZoomNormal, Ctrl+A=SelectAll, Ctrl+B=BookmarkLine, Ctrl+C=Copy, Ctrl+E=MacroExecute, Ctrl+F=FindDialog, Ctrl+G=GoToDialog, Ctrl+H=ReplaceDialog, Ctrl+I=AutoIndentChars, Ctrl+M=MacroRecord, Ctrl+N=GoNextBookmark, Ctrl+R=Redo, Ctrl+U=UpperCase, Ctrl+V=Paste, Ctrl+X=Cut, Ctrl+Z=Undo, Ctrl+Add=ZoomIn, Ctrl+Subtract=ZoomOut, Ctrl+OemMinus=NavigateBackward, Ctrl+Shift+End=GoLastLineWithSelection, Ctrl+Shift+Home=GoFirstLineWithSelection, Ctrl+Shift+Left=GoWordLeftWithSelection, Ctrl+Shift+Right=GoWordRightWithSelection, Ctrl+Shift+B=UnbookmarkLine, Ctrl+Shift+C=CommentSelected, Ctrl+Shift+N=GoPrevBookmark, Ctrl+Shift+U=LowerCase, Ctrl+Shift+OemMinus=NavigateForward, Alt+Back=Undo, Alt+Up=MoveSelectedLinesUp, Alt+Down=MoveSelectedLinesDown, Alt+F=FindChar, Alt+Shift+Left=GoLeft_ColumnSelectionMode, Alt+Shift+Up=GoUp_ColumnSelectionMode, Alt+Shift+Right=GoRight_ColumnSelectionMode, Alt+Shift+Down=GoDown_ColumnSelectionMode"
            )]
        public string Hotkeys
        {
            get
            {
                return this.HotkeysMapping.ToString();
            }
            set
            {
                this.HotkeysMapping = HotkeysMapping.Parse(value);
            }
        }

        /// <summary>
        /// 	Hotkeys mapping
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public HotkeysMapping HotkeysMapping { get; set; }

        /// <summary>
        /// 	Default text style
        /// 	This style is using when no one other TextStyle is not defined in Char.style
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public TextStyle DefaultStyle
        {
            get
            {
                return this.lines.DefaultStyle;
            }
            set
            {
                this.lines.DefaultStyle = value;
            }
        }

        /// <summary>
        /// 	Style for rendering Selection area
        /// </summary>
        [Browsable(false)]
        public SelectionStyle SelectionStyle { get; set; }

        /// <summary>
        /// 	Style for folded block rendering
        /// </summary>
        [Browsable(false)]
        public TextStyle FoldedBlockStyle { get; set; }

        /// <summary>
        /// 	Style for brackets highlighting
        /// </summary>
        [Browsable(false)]
        public MarkerStyle BracketsStyle { get; set; }

        /// <summary>
        /// 	Style for alternative brackets highlighting
        /// </summary>
        [Browsable(false)]
        public MarkerStyle BracketsStyle2 { get; set; }

        /// <summary>
        /// 	Opening bracket for brackets highlighting.
        /// 	Set to '\x0' for disable brackets highlighting.
        /// </summary>
        [DefaultValue('\x0')]
        [Description("Opening bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")]
        public char LeftBracket { get; set; }

        /// <summary>
        /// 	Closing bracket for brackets highlighting.
        /// 	Set to '\x0' for disable brackets highlighting.
        /// </summary>
        [DefaultValue('\x0')]
        [Description("Closing bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")]
        public char RightBracket { get; set; }

        /// <summary>
        /// 	Alternative opening bracket for brackets highlighting.
        /// 	Set to '\x0' for disable brackets highlighting.
        /// </summary>
        [DefaultValue('\x0')]
        [Description(
            "Alternative opening bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")]
        public char LeftBracket2 { get; set; }

        /// <summary>
        /// 	Alternative closing bracket for brackets highlighting.
        /// 	Set to '\x0' for disable brackets highlighting.
        /// </summary>
        [DefaultValue('\x0')]
        [Description(
            "Alternative closing bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")]
        public char RightBracket2 { get; set; }

        /// <summary>
        /// 	Comment line prefix.
        /// </summary>
        [DefaultValue("//")]
        [Description("Comment line prefix.")]
        public string CommentPrefix { get; set; }

        /// <summary>
        /// 	This property specifies which part of the text will be highlighted as you type (by built-in highlighter).
        /// </summary>
        /// <remarks>
        /// 	When a user enters text, a component refreshes highlighting (because the text was changed).
        /// 	This property specifies exactly which section of the text will be re-highlighted.
        /// 	This can be useful to highlight multi-line comments, for example.
        /// </remarks>
        [DefaultValue(typeof(HighlightingRangeType), "ChangedRange")]
        [Description("This property specifies which part of the text will be highlighted as you type.")]
        public HighlightingRangeType HighlightingRangeType { get; set; }

        /// <summary>
        /// 	Is keyboard in replace mode (wide caret) ?
        /// </summary>
        [Browsable(false)]
        public bool IsReplaceMode
        {
            get
            {
                return this.isReplaceMode && this.Selection.IsEmpty && (!this.Selection.ColumnSelectionMode)
                       && this.Selection.Start.iChar < this.lines[this.Selection.Start.iLine].Count;
            }
            set
            {
                this.isReplaceMode = value;
            }
        }

        /// <summary>
        /// 	Allows text rendering several styles same time.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(false)]
        [Description("Allows text rendering several styles same time.")]
        public bool AllowSeveralTextStyleDrawing { get; set; }

        /// <summary>
        /// 	Allows to record macros.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(true)]
        [Description("Allows to record macros.")]
        public bool AllowMacroRecording
        {
            get
            {
                return this.macrosManager.AllowMacroRecordingByUser;
            }
            set
            {
                this.macrosManager.AllowMacroRecordingByUser = value;
            }
        }

        /// <summary>
        /// 	Allows AutoIndent. Inserts spaces before new line.
        /// </summary>
        [DefaultValue(true)]
        [Description("Allows auto indent. Inserts spaces before line chars.")]
        public bool AutoIndent { get; set; }

        /// <summary>
        /// 	Does autoindenting in existing lines. It works only if AutoIndent is True.
        /// </summary>
        [DefaultValue(true)]
        [Description("Does autoindenting in existing lines. It works only if AutoIndent is True.")]
        public bool AutoIndentExistingLines { get; set; }

        /// <summary>
        /// 	Minimal delay(ms) for delayed events (except TextChangedDelayed).
        /// </summary>
        [Browsable(true)]
        [DefaultValue(100)]
        [Description("Minimal delay(ms) for delayed events (except TextChangedDelayed).")]
        public int DelayedEventsInterval
        {
            get
            {
                return this.timer.Interval;
            }
            set
            {
                this.timer.Interval = value;
            }
        }

        /// <summary>
        /// 	Minimal delay(ms) for TextChangedDelayed event.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(100)]
        [Description("Minimal delay(ms) for TextChangedDelayed event.")]
        public int DelayedTextChangedInterval
        {
            get
            {
                return this.timer2.Interval;
            }
            set
            {
                this.timer2.Interval = value;
            }
        }

        /// <summary>
        /// 	Language for highlighting by built-in highlighter.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(typeof(Language), "Custom")]
        [Description("Language for highlighting by built-in highlighter.")]
        public Language Language
        {
            get
            {
                return this.language;
            }
            set
            {
                this.language = value;
                if (this.SyntaxHighlighter != null)
                {
                    this.SyntaxHighlighter.InitStyleSchema(this.language);
                }
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Syntax Highlighter
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public SyntaxHighlighter SyntaxHighlighter { get; set; }

        /// <summary>
        /// 	XML file with description of syntax highlighting.
        /// 	This property works only with Language == Language.Custom.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(null)]
        [Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
        [Description(
            "XML file with description of syntax highlighting. This property works only with Language == Language.Custom."
            )]
        public string DescriptionFile
        {
            get
            {
                return this.descriptionFile;
            }
            set
            {
                this.descriptionFile = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Position of left highlighted bracket.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Range LeftBracketPosition
        {
            get
            {
                return this.leftBracketPosition;
            }
        }

        /// <summary>
        /// 	Position of right highlighted bracket.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Range RightBracketPosition
        {
            get
            {
                return this.rightBracketPosition;
            }
        }

        /// <summary>
        /// 	Position of left highlighted alternative bracket.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Range LeftBracketPosition2
        {
            get
            {
                return this.leftBracketPosition2;
            }
        }

        /// <summary>
        /// 	Position of right highlighted alternative bracket.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Range RightBracketPosition2
        {
            get
            {
                return this.rightBracketPosition2;
            }
        }

        /// <summary>
        /// 	Start line index of current highlighted folding area. Return -1 if start of area is not found.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int StartFoldingLine
        {
            get
            {
                return this.startFoldingLine;
            }
        }

        /// <summary>
        /// 	End line index of current highlighted folding area. Return -1 if end of area is not found.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int EndFoldingLine
        {
            get
            {
                return this.endFoldingLine;
            }
        }

        /// <summary>
        /// 	TextSource
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public TextSource TextSource
        {
            get
            {
                return this.lines;
            }
            set
            {
                this.InitTextSource(value);
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool HasSourceTextBox
        {
            get
            {
                return this.SourceTextBox != null;
            }
        }

        /// <summary>
        /// 	The source of the text.
        /// 	Allows to get text from other FastColoredTextBox.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(null)]
        [Description("Allows to get text from other FastColoredTextBox.")]
        //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public FastColoredTextBox SourceTextBox
        {
            get
            {
                return this.sourceTextBox;
            }
            set
            {
                if (value == this.sourceTextBox)
                {
                    return;
                }

                this.sourceTextBox = value;

                if (this.sourceTextBox == null)
                {
                    this.InitTextSource(this.CreateTextSource());
                    this.lines.InsertLine(0, this.TextSource.CreateLine());
                    this.IsChanged = false;
                }
                else
                {
                    this.InitTextSource(this.SourceTextBox.TextSource);
                    this.isChanged = false;
                }
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Returns current visible range of text
        /// </summary>
        [Browsable(false)]
        public Range VisibleRange
        {
            get
            {
                if (this.visibleRange != null)
                {
                    return this.visibleRange;
                }
                return this.GetRange(
                    this.PointToPlace(new Point(this.LeftIndent, 0)),
                    this.PointToPlace(new Point(this.ClientSize.Width, this.ClientSize.Height)));
            }
        }

        /// <summary>
        /// 	Current selection range
        /// </summary>
        [Browsable(false)]
        public Range Selection
        {
            get
            {
                return this.selection;
            }
            set
            {
                this.selection.BeginUpdate();
                this.selection.Start = value.Start;
                this.selection.End = value.End;
                this.selection.EndUpdate();
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Background color.
        /// 	It is used if BackBrush is null.
        /// </summary>
        [DefaultValue(typeof(Color), "White")]
        [Description("Background color.")]
        public override Color BackColor
        {
            get
            {
                return base.BackColor;
            }
            set
            {
                base.BackColor = value;
            }
        }

        /// <summary>
        /// 	Background brush.
        /// 	If Null then BackColor is used.
        /// </summary>
        [Browsable(false)]
        public Brush BackBrush
        {
            get
            {
                return this.backBrush;
            }
            set
            {
                this.backBrush = value;
                this.Invalidate();
            }
        }

        [Browsable(true)]
        [DefaultValue(true)]
        [Description("Scollbars visibility.")]
        public bool ShowScrollBars
        {
            get
            {
                return this.scrollBars;
            }
            set
            {
                if (value == this.scrollBars)
                {
                    return;
                }
                this.scrollBars = value;
                this.needRecalc = true;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Multiline
        /// </summary>
        [Browsable(true)]
        [DefaultValue(true)]
        [Description("Multiline mode.")]
        public bool Multiline
        {
            get
            {
                return this.multiline;
            }
            set
            {
                if (this.multiline == value)
                {
                    return;
                }
                this.multiline = value;
                this.needRecalc = true;
                if (this.multiline)
                {
                    base.AutoScroll = true;
                    this.ShowScrollBars = true;
                }
                else
                {
                    base.AutoScroll = false;
                    this.ShowScrollBars = false;
                    if (this.lines.Count > 1)
                    {
                        this.lines.RemoveLine(1, this.lines.Count - 1);
                    }
                    this.lines.Manager.ClearHistory();
                }
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	WordWrap.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(false)]
        [Description("WordWrap.")]
        public bool WordWrap
        {
            get
            {
                return this.wordWrap;
            }
            set
            {
                if (this.wordWrap == value)
                {
                    return;
                }
                this.wordWrap = value;
                if (this.wordWrap)
                {
                    this.Selection.ColumnSelectionMode = false;
                }
                this.NeedRecalc(false, true);
                //RecalcWordWrap(0, LinesCount - 1);
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	WordWrap mode.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(typeof(WordWrapMode), "WordWrapControlWidth")]
        [Description("WordWrap mode.")]
        public WordWrapMode WordWrapMode
        {
            get
            {
                return this.wordWrapMode;
            }
            set
            {
                if (this.wordWrapMode == value)
                {
                    return;
                }
                this.wordWrapMode = value;
                this.NeedRecalc(false, true);
                //RecalcWordWrap(0, LinesCount - 1);
                this.Invalidate();
            }
        }

        private bool selectionHighlightingForLineBreaksEnabled;

        /// <summary>
        /// 	If <c>true</c> then line breaks included into the selection will be selected too.
        /// 	Then line breaks will be shown as selected blank character.
        /// </summary>
        [DefaultValue(true)]
        [Description(
            "If enabled then line ends included into the selection will be selected too. "
            + "Then line ends will be shown as selected blank character.")]
        public bool SelectionHighlightingForLineBreaksEnabled
        {
            get
            {
                return this.selectionHighlightingForLineBreaksEnabled;
            }
            set
            {
                this.selectionHighlightingForLineBreaksEnabled = value;
                this.Invalidate();
            }
        }

        [Browsable(false)]
        public FindForm findForm { get; private set; }

        [Browsable(false)]
        public ReplaceForm replaceForm { get; private set; }

        /// <summary>
        /// 	Do not change this property
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override bool AutoScroll
        {
            get
            {
                return base.AutoScroll;
            }
            set
            {
                ;
            }
        }

        /// <summary>
        /// 	Count of lines
        /// </summary>
        [Browsable(false)]
        public int LinesCount
        {
            get
            {
                return this.lines.Count;
            }
        }

        /// <summary>
        /// 	Gets or sets char and styleId for given place
        /// 	This property does not fire OnTextChanged event
        /// </summary>
        public Char this[Place place]
        {
            get
            {
                return this.lines[place.iLine][place.iChar];
            }
            set
            {
                this.lines[place.iLine][place.iChar] = value;
            }
        }

        /// <summary>
        /// 	Gets Line
        /// </summary>
        public Line this[int iLine]
        {
            get
            {
                return this.lines[iLine];
            }
        }

        /// <summary>
        /// 	Text of control
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Editor(
            "System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            , typeof(UITypeEditor))]
        [SettingsBindable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("Text of the control.")]
        [Bindable(true)]
        public override string Text
        {
            get
            {
                if (this.LinesCount == 0)
                {
                    return "";
                }
                var sel = new Range(this);
                sel.SelectAll();
                return sel.Text;
            }

            set
            {
                if (value == this.Text && value != "")
                {
                    return;
                }

                this.SetAsCurrentTB();

                this.Selection.ColumnSelectionMode = false;

                this.Selection.BeginUpdate();
                try
                {
                    this.Selection.SelectAll();
                    this.InsertText(value);
                    this.GoHome();
                }
                finally
                {
                    this.Selection.EndUpdate();
                }
            }
        }

        /// <summary>
        /// 	Text lines
        /// </summary>
        [Browsable(false)]
        public IList<string> Lines
        {
            get
            {
                return this.lines.GetLines();
            }
        }

        /// <summary>
        /// 	Gets colored text as HTML
        /// </summary>
        /// <remarks>
        /// 	For more flexibility you can use ExportToHTML class also
        /// </remarks>
        [Browsable(false)]
        public string Html
        {
            get
            {
                var exporter = new ExportToHTML();
                exporter.UseNbsp = false;
                exporter.UseStyleTag = false;
                exporter.UseBr = false;
                return "<pre>" + exporter.GetHtml(this) + "</pre>";
            }
        }

        /// <summary>
        /// 	Gets colored text as RTF
        /// </summary>
        /// <remarks>
        /// 	For more flexibility you can use ExportToRTF class also
        /// </remarks>
        [Browsable(false)]
        public string Rtf
        {
            get
            {
                var exporter = new ExportToRTF();
                return exporter.GetRtf(this);
            }
        }

        /// <summary>
        /// 	Text of current selection
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string SelectedText
        {
            get
            {
                return this.Selection.Text;
            }
            set
            {
                this.InsertText(value);
            }
        }

        /// <summary>
        /// 	Start position of selection
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int SelectionStart
        {
            get
            {
                return Math.Min(this.PlaceToPosition(this.Selection.Start), this.PlaceToPosition(this.Selection.End));
            }
            set
            {
                this.Selection.Start = this.PositionToPlace(value);
            }
        }

        /// <summary>
        /// 	Length of selected text
        /// </summary>
        [Browsable(false)]
        [DefaultValue(0)]
        public int SelectionLength
        {
            get
            {
                return Math.Abs(this.PlaceToPosition(this.Selection.Start) - this.PlaceToPosition(this.Selection.End));
            }
            set
            {
                if (value > 0)
                {
                    this.Selection.End = this.PositionToPlace(this.SelectionStart + value);
                }
            }
        }

        /// <summary>
        /// 	Font
        /// </summary>
        /// <remarks>
        /// 	Use only monospaced font
        /// </remarks>
        [DefaultValue(typeof(Font), "Courier New, 9.75")]
        public override Font Font
        {
            get
            {
                return this.BaseFont;
            }
            set
            {
                this.originalFont = (Font)value.Clone();
                this.SetFont(value);
            }
        }

        /// <summary>
        /// 	Font
        /// </summary>
        /// <remarks>
        /// 	Use only monospaced font
        /// </remarks>
        [DefaultValue(typeof(Font), "Courier New, 9.75")]
        private Font BaseFont { get; set; }

        private void SetFont(Font newFont)
        {
            this.BaseFont = newFont;
            //check monospace font
            SizeF sizeM = GetCharSize(this.BaseFont, 'M');
            SizeF sizeDot = GetCharSize(this.BaseFont, '.');
            if (sizeM != sizeDot)
            {
                this.BaseFont = new Font(
                    "Courier New", this.BaseFont.SizeInPoints, FontStyle.Regular, GraphicsUnit.Point);
            }
            //clac size
            SizeF size = GetCharSize(this.BaseFont, 'M');
            this.CharWidth = (int)Math.Round(size.Width * 1f /*0.85*/) - 1 /*0*/;
            this.CharHeight = this.lineInterval + (int)Math.Round(size.Height * 1f /*0.9*/) - 1 /*0*/;
            //
            //if (wordWrap)
            //    RecalcWordWrap(0, Lines.Count - 1);
            this.NeedRecalc(false, this.wordWrap);
            //
            this.Invalidate();
        }

        public new Size AutoScrollMinSize
        {
            set
            {
                if (this.scrollBars)
                {
                    if (!base.AutoScroll)
                    {
                        base.AutoScroll = true;
                    }
                    Size newSize = value;
                    if (this.WordWrap && this.WordWrapMode != FastColoredTextBoxNS.WordWrapMode.Custom)
                    {
                        int maxWidth = this.GetMaxLineWordWrapedWidth();
                        newSize = new Size(Math.Min(newSize.Width, maxWidth), newSize.Height);
                    }
                    base.AutoScrollMinSize = newSize;
                }
                else
                {
                    if (base.AutoScroll)
                    {
                        base.AutoScroll = false;
                    }
                    base.AutoScrollMinSize = new Size(0, 0);
                    this.VerticalScroll.Visible = false;
                    this.HorizontalScroll.Visible = false;
                    this.VerticalScroll.Maximum = Math.Max(0, value.Height - this.ClientSize.Height);
                    this.HorizontalScroll.Maximum = Math.Max(0, value.Width - this.ClientSize.Width);
                    this.localAutoScrollMinSize = value;
                }
            }

            get
            {
                if (this.scrollBars)
                {
                    return base.AutoScrollMinSize;
                }
                else
                {
                    //return new Size(HorizontalScroll.Maximum, VerticalScroll.Maximum);
                    return this.localAutoScrollMinSize;
                }
            }
        }

        /// <summary>
        /// 	Indicates that IME is allowed (for CJK language entering)
        /// </summary>
        [Browsable(false)]
        public bool ImeAllowed
        {
            get
            {
                return this.ImeMode != ImeMode.Disable && this.ImeMode != ImeMode.Off
                       && this.ImeMode != ImeMode.NoControl;
            }
        }

        /// <summary>
        /// 	Is undo enabled?
        /// </summary>
        [Browsable(false)]
        public bool UndoEnabled
        {
            get
            {
                return this.lines.Manager.UndoEnabled;
            }
        }

        /// <summary>
        /// 	Is redo enabled?
        /// </summary>
        [Browsable(false)]
        public bool RedoEnabled
        {
            get
            {
                return this.lines.Manager.RedoEnabled;
            }
        }

        private int LeftIndentLine
        {
            get
            {
                return this.LeftIndent - minLeftIndent / 2 - 3;
            }
        }

        /// <summary>
        /// 	Range of all text
        /// </summary>
        [Browsable(false)]
        public Range Range
        {
            get
            {
                return new Range(
                    this, new Place(0, 0), new Place(this.lines[this.lines.Count - 1].Count, this.lines.Count - 1));
            }
        }

        /// <summary>
        /// 	Color of selected area
        /// </summary>
        [DefaultValue(typeof(Color), "Blue")]
        [Description("Color of selected area.")]
        public virtual Color SelectionColor
        {
            get
            {
                return this.selectionColor;
            }
            set
            {
                this.selectionColor = value;
                if (this.selectionColor.A == 255)
                {
                    this.selectionColor = Color.FromArgb(60, this.selectionColor);
                }
                this.SelectionStyle = new SelectionStyle(new SolidBrush(this.selectionColor));
                this.Invalidate();
            }
        }

        public override Cursor Cursor
        {
            get
            {
                return base.Cursor;
            }
            set
            {
                this.defaultCursor = value;
                base.Cursor = value;
            }
        }

        /// <summary>
        /// 	Reserved space for line number characters.
        /// 	If smaller than needed (e. g. line count >= 10 and this value set to 1) this value will have no impact.
        /// 	If you want to reserve space, e. g. for line numbers >= 10 or >= 100 than you can set this value to 2 or 3 or higher.
        /// </summary>
        [DefaultValue(1)]
        [Description(
            "Reserved space for line number characters. If smaller than needed (e. g. line count >= 10 and "
            + "this value set to 1) this value will have no impact. If you want to reserve space, e. g. for line "
            + "numbers >= 10 or >= 100, than you can set this value to 2 or 3 or higher.")]
        public int ReservedCountOfLineNumberChars
        {
            get
            {
                return this.reservedCountOfLineNumberChars;
            }
            set
            {
                this.reservedCountOfLineNumberChars = value;
                this.NeedRecalc();
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Occurs when mouse is moving over text and tooltip is needed
        /// </summary>
        [Browsable(true)]
        [Description("Occurs when mouse is moving over text and tooltip is needed.")]
        public event EventHandler<ToolTipNeededEventArgs> ToolTipNeeded;

        /// <summary>
        /// 	Removes all hints
        /// </summary>
        public void ClearHints()
        {
            if (this.Hints != null)
            {
                this.Hints.Clear();
            }
        }

        /// <summary>
        /// 	Add and shows the hint
        /// </summary>
        /// <param name="range"> Linked range </param>
        /// <param name="innerControl"> Inner control </param>
        /// <param name="scrollToHint"> Scrolls textbox to the hint </param>
        /// <param name="inline"> Inlining. If True then hint will moves apart text </param>
        /// <param name="dock"> Docking. If True then hint will fill whole line </param>
        public virtual Hint AddHint(Range range, Control innerControl, bool scrollToHint, bool inline, bool dock)
        {
            var hint = new Hint(range, innerControl, inline, dock);
            this.Hints.Add(hint);
            if (scrollToHint)
            {
                hint.DoVisible();
            }

            return hint;
        }

        /// <summary>
        /// 	Add and shows the hint
        /// </summary>
        /// <param name="range"> Linked range </param>
        /// <param name="innerControl"> Inner control </param>
        public Hint AddHint(Range range, Control innerControl)
        {
            return AddHint(range, innerControl, true, true, true);
        }

        /// <summary>
        /// 	Add and shows simple text hint
        /// </summary>
        /// <param name="range"> Linked range </param>
        /// <param name="text"> Text of simple hint </param>
        /// <param name="scrollToHint"> Scrolls textbox to the hint </param>
        /// <param name="inline"> Inlining. If True then hint will moves apart text </param>
        /// <param name="dock"> Docking. If True then hint will fill whole line </param>
        public virtual Hint AddHint(Range range, string text, bool scrollToHint, bool inline, bool dock)
        {
            var hint = new Hint(range, text, inline, dock);
            this.Hints.Add(hint);
            if (scrollToHint)
            {
                hint.DoVisible();
            }

            return hint;
        }

        /// <summary>
        /// 	Add and shows simple text hint
        /// </summary>
        /// <param name="range"> Linked range </param>
        /// <param name="text"> Text of simple hint </param>
        public Hint AddHint(Range range, string text)
        {
            return AddHint(range, text, true, true, true);
        }

        /// <summary>
        /// 	Occurs when user click on the hint
        /// </summary>
        /// <param name="hint"> </param>
        public virtual void OnHintClick(Hint hint)
        {
            if (this.HintClick != null)
            {
                this.HintClick(this, new HintClickEventArgs(hint));
            }
        }

        private void timer3_Tick(object sender, EventArgs e)
        {
            this.timer3.Stop();
            this.OnToolTip();
        }

        protected virtual void OnToolTip()
        {
            if (this.ToolTip == null)
            {
                return;
            }
            if (this.ToolTipNeeded == null)
            {
                return;
            }

            //get place under mouse
            Place place = this.PointToPlace(this.lastMouseCoord);

            //check distance
            Point p = this.PlaceToPoint(place);
            if (Math.Abs(p.X - this.lastMouseCoord.X) > this.CharWidth * 2
                || Math.Abs(p.Y - this.lastMouseCoord.Y) > this.CharHeight * 2)
            {
                return;
            }
            //get word under mouse
            var r = new Range(this, place, place);
            string hoveredWord = r.GetFragment("[a-zA-Z]").Text;
            //event handler
            var ea = new ToolTipNeededEventArgs(place, hoveredWord);
            this.ToolTipNeeded(this, ea);

            if (ea.ToolTipText != null)
            {
                //show tooltip
                this.ToolTip.ToolTipTitle = ea.ToolTipTitle;
                this.ToolTip.ToolTipIcon = ea.ToolTipIcon;
                //ToolTip.SetToolTip(this, ea.ToolTipText);
                this.ToolTip.Show(
                    ea.ToolTipText, this, new Point(this.lastMouseCoord.X, this.lastMouseCoord.Y + this.CharHeight));
            }
        }

        /// <summary>
        /// 	Occurs when VisibleRange is changed
        /// </summary>
        public virtual void OnVisibleRangeChanged()
        {
            this.needRecalcFoldingLines = true;

            this.needRiseVisibleRangeChangedDelayed = true;
            this.ResetTimer(this.timer);
            if (this.VisibleRangeChanged != null)
            {
                this.VisibleRangeChanged(this, new EventArgs());
            }
        }

        /// <summary>
        /// 	Invalidates the entire surface of the control and causes the control to be redrawn.
        /// 	This method is thread safe and does not require Invoke.
        /// </summary>
        public new void Invalidate()
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(this.Invalidate));
            }
            else
            {
                base.Invalidate();
            }
        }

        protected virtual void OnCharSizeChanged()
        {
            this.VerticalScroll.SmallChange = this.charHeight;
            this.VerticalScroll.LargeChange = 10 * this.charHeight;
            this.HorizontalScroll.SmallChange = this.CharWidth;
        }

        /// <summary>
        /// 	HintClick event.
        /// 	It occurs if user click on the hint.
        /// </summary>
        [Browsable(true)]
        [Description("It occurs if user click on the hint.")]
        public event EventHandler<HintClickEventArgs> HintClick;

        /// <summary>
        /// 	TextChanged event.
        /// 	It occurs after insert, delete, clear, undo and redo operations.
        /// </summary>
        [Browsable(true)]
        [Description("It occurs after insert, delete, clear, undo and redo operations.")]
        public new event EventHandler<TextChangedEventArgs> TextChanged;

        /// <summary>
        /// 	Fake event for correct data binding
        /// </summary>
        [Browsable(false)]
        internal event EventHandler BindingTextChanged;

        /// <summary>
        /// 	Occurs when user paste text from clipboard
        /// </summary>
        [Description("Occurs when user paste text from clipboard")]
        public event EventHandler<TextChangingEventArgs> Pasting;

        /// <summary>
        /// 	TextChanging event.
        /// 	It occurs before insert, delete, clear, undo and redo operations.
        /// </summary>
        [Browsable(true)]
        [Description("It occurs before insert, delete, clear, undo and redo operations.")]
        public event EventHandler<TextChangingEventArgs> TextChanging;

        /// <summary>
        /// 	SelectionChanged event.
        /// 	It occurs after changing of selection.
        /// </summary>
        [Browsable(true)]
        [Description("It occurs after changing of selection.")]
        public event EventHandler SelectionChanged;

        /// <summary>
        /// 	VisibleRangeChanged event.
        /// 	It occurs after changing of visible range.
        /// </summary>
        [Browsable(true)]
        [Description("It occurs after changing of visible range.")]
        public event EventHandler VisibleRangeChanged;

        /// <summary>
        /// 	TextChangedDelayed event. 
        /// 	It occurs after insert, delete, clear, undo and redo operations. 
        /// 	This event occurs with a delay relative to TextChanged, and fires only once.
        /// </summary>
        [Browsable(true)]
        [Description(
            "It occurs after insert, delete, clear, undo and redo operations. This event occurs with a delay relative to TextChanged, and fires only once."
            )]
        public event EventHandler<TextChangedEventArgs> TextChangedDelayed;

        /// <summary>
        /// 	SelectionChangedDelayed event.
        /// 	It occurs after changing of selection.
        /// 	This event occurs with a delay relative to SelectionChanged, and fires only once.
        /// </summary>
        [Browsable(true)]
        [Description(
            "It occurs after changing of selection. This event occurs with a delay relative to SelectionChanged, and fires only once."
            )]
        public event EventHandler SelectionChangedDelayed;

        /// <summary>
        /// 	VisibleRangeChangedDelayed event.
        /// 	It occurs after changing of visible range.
        /// 	This event occurs with a delay relative to VisibleRangeChanged, and fires only once.
        /// </summary>
        [Browsable(true)]
        [Description(
            "It occurs after changing of visible range. This event occurs with a delay relative to VisibleRangeChanged, and fires only once."
            )]
        public event EventHandler VisibleRangeChangedDelayed;

        /// <summary>
        /// 	It occurs when user click on VisualMarker.
        /// </summary>
        [Browsable(true)]
        [Description("It occurs when user click on VisualMarker.")]
        public event EventHandler<VisualMarkerEventArgs> VisualMarkerClick;

        /// <summary>
        /// 	It occurs when visible char is enetering (alphabetic, digit, punctuation, DEL, BACKSPACE)
        /// </summary>
        /// <remarks>
        /// 	Set Handle to True for cancel key
        /// </remarks>
        [Browsable(true)]
        [Description("It occurs when visible char is enetering (alphabetic, digit, punctuation, DEL, BACKSPACE).")]
        public event KeyPressEventHandler KeyPressing;

        /// <summary>
        /// 	It occurs when visible char is enetered (alphabetic, digit, punctuation, DEL, BACKSPACE)
        /// </summary>
        [Browsable(true)]
        [Description("It occurs when visible char is enetered (alphabetic, digit, punctuation, DEL, BACKSPACE).")]
        public event KeyPressEventHandler KeyPressed;

        /// <summary>
        /// 	It occurs when calculates AutoIndent for new line
        /// </summary>
        [Browsable(true)]
        [Description("It occurs when calculates AutoIndent for new line.")]
        public event EventHandler<AutoIndentEventArgs> AutoIndentNeeded;

        /// <summary>
        /// 	It occurs when line background is painting
        /// </summary>
        [Browsable(true)]
        [Description("It occurs when line background is painting.")]
        public event EventHandler<PaintLineEventArgs> PaintLine;

        /// <summary>
        /// 	Occurs when line was inserted/added
        /// </summary>
        [Browsable(true)]
        [Description("Occurs when line was inserted/added.")]
        public event EventHandler<LineInsertedEventArgs> LineInserted;

        /// <summary>
        /// 	Occurs when line was removed
        /// </summary>
        [Browsable(true)]
        [Description("Occurs when line was removed.")]
        public event EventHandler<LineRemovedEventArgs> LineRemoved;

        /// <summary>
        /// 	Occurs when current highlighted folding area is changed.
        /// 	Current folding area see in StartFoldingLine and EndFoldingLine.
        /// </summary>
        /// <remarks>
        /// </remarks>
        [Browsable(true)]
        [Description("Occurs when current highlighted folding area is changed.")]
        public event EventHandler<EventArgs> FoldingHighlightChanged;

        /// <summary>
        /// 	Occurs when undo/redo stack is changed
        /// </summary>
        /// <remarks>
        /// </remarks>
        [Browsable(true)]
        [Description("Occurs when undo/redo stack is changed.")]
        public event EventHandler<EventArgs> UndoRedoStateChanged;

        /// <summary>
        /// 	Occurs when component was zoomed
        /// </summary>
        [Browsable(true)]
        [Description("Occurs when component was zoomed.")]
        public event EventHandler ZoomChanged;

        /// <summary>
        /// 	Occurs when user pressed key, that specified as CustomAction
        /// </summary>
        [Browsable(true)]
        [Description("Occurs when user pressed key, that specified as CustomAction.")]
        public event EventHandler<CustomActionEventArgs> CustomAction;

        /// <summary>
        /// 	Occurs when scroolbars are updated
        /// </summary>
        [Browsable(true)]
        [Description("Occurs when scroolbars are updated.")]
        public event EventHandler ScrollbarsUpdated;

        /// <summary>
        /// 	Occurs when custom wordwrap is needed
        /// </summary>
        [Browsable(true)]
        [Description("Occurs when custom wordwrap is needed.")]
        public event EventHandler<WordWrapNeededEventArgs> WordWrapNeeded;

        /// <summary>
        /// 	Returns list of styles of given place
        /// </summary>
        public List<Style> GetStylesOfChar(Place place)
        {
            var result = new List<Style>();
            if (place.iLine < this.LinesCount && place.iChar < this[place.iLine].Count)
            {
#if Styles32
                var s = (uint) this[place].style;
                for (int i = 0; i < 32; i++)
                    if ((s & ((uint) 1) << i) != 0)
                        result.Add(Styles[i]);
#else
                var s = (ushort)this[place].style;
                for (int i = 0; i < 16; i++)
                {
                    if ((s & (1) << i) != 0)
                    {
                        result.Add(this.Styles[i]);
                    }
                }
#endif
            }

            return result;
        }

        protected virtual TextSource CreateTextSource()
        {
            return new TextSource(this);
        }

        private void SetAsCurrentTB()
        {
            this.TextSource.CurrentTB = this;
        }

        protected void InitTextSource(TextSource ts)
        {
            if (this.lines != null)
            {
                ts.LineInserted -= this.ts_LineInserted;
                ts.LineRemoved -= this.ts_LineRemoved;
                ts.TextChanged -= this.ts_TextChanged;
                ts.RecalcNeeded -= this.ts_RecalcNeeded;
                ts.RecalcWordWrap -= this.ts_RecalcWordWrap;
                ts.TextChanging -= this.ts_TextChanging;

                this.lines.Dispose();
            }

            this.LineInfos.Clear();
            this.ClearHints();
            if (this.Bookmarks != null)
            {
                this.Bookmarks.Clear();
            }

            this.lines = ts;

            if (ts != null)
            {
                ts.LineInserted += this.ts_LineInserted;
                ts.LineRemoved += this.ts_LineRemoved;
                ts.TextChanged += this.ts_TextChanged;
                ts.RecalcNeeded += this.ts_RecalcNeeded;
                ts.RecalcWordWrap += this.ts_RecalcWordWrap;
                ts.TextChanging += this.ts_TextChanging;

                while (this.LineInfos.Count < ts.Count)
                {
                    this.LineInfos.Add(new LineInfo(-1));
                }
            }

            this.isChanged = false;
            this.needRecalc = true;
        }

        private void ts_RecalcWordWrap(object sender, TextSource.TextChangedEventArgs e)
        {
            this.RecalcWordWrap(e.iFromLine, e.iToLine);
        }

        private void ts_TextChanging(object sender, TextChangingEventArgs e)
        {
            if (this.TextSource.CurrentTB == this)
            {
                string text = e.InsertingText;
                this.OnTextChanging(ref text);
                e.InsertingText = text;
            }
        }

        private void ts_RecalcNeeded(object sender, TextSource.TextChangedEventArgs e)
        {
            if (e.iFromLine == e.iToLine && !this.WordWrap && this.lines.Count > minLinesForAccuracy)
            {
                this.RecalcScrollByOneLine(e.iFromLine);
            }
            else
            {
                this.needRecalc = true;
            }
        }

        /// <summary>
        /// 	Call this method if the recalc of the position of lines is needed.
        /// </summary>
        public void NeedRecalc()
        {
            this.NeedRecalc(false);
        }

        /// <summary>
        /// 	Call this method if the recalc of the position of lines is needed.
        /// </summary>
        public void NeedRecalc(bool forced)
        {
            this.NeedRecalc(forced, false);
        }

        /// <summary>
        /// 	Call this method if the recalc of the position of lines is needed.
        /// </summary>
        public void NeedRecalc(bool forced, bool wordWrapRecalc)
        {
            this.needRecalc = true;

            if (wordWrapRecalc)
            {
                this.needRecalcWordWrapInterval = new Point(0, this.LinesCount - 1);
                this.needRecalcWordWrap = true;
            }

            if (forced)
            {
                this.Recalc();
            }
        }

        private void ts_TextChanged(object sender, TextSource.TextChangedEventArgs e)
        {
            if (e.iFromLine == e.iToLine && !this.WordWrap)
            {
                this.RecalcScrollByOneLine(e.iFromLine);
            }
            else
            {
                this.needRecalc = true;
            }

            this.Invalidate();
            if (this.TextSource.CurrentTB == this)
            {
                this.OnTextChanged(e.iFromLine, e.iToLine);
            }
        }

        private void ts_LineRemoved(object sender, LineRemovedEventArgs e)
        {
            this.LineInfos.RemoveRange(e.Index, e.Count);
            this.OnLineRemoved(e.Index, e.Count, e.RemovedLineUniqueIds);
        }

        private void ts_LineInserted(object sender, LineInsertedEventArgs e)
        {
            var newState = VisibleState.Visible;
            if (e.Index >= 0 && e.Index < this.LineInfos.Count
                && this.LineInfos[e.Index].VisibleState == VisibleState.Hidden)
            {
                newState = VisibleState.Hidden;
            }

            var temp = new List<LineInfo>(e.Count);
            for (int i = 0; i < e.Count; i++)
            {
                temp.Add(new LineInfo(-1) { VisibleState = newState });
            }
            this.LineInfos.InsertRange(e.Index, temp);

            this.OnLineInserted(e.Index, e.Count);
        }

        /// <summary>
        /// 	Navigates forward (by Line.LastVisit property)
        /// </summary>
        public bool NavigateForward()
        {
            DateTime min = DateTime.Now;
            int iLine = -1;
            for (int i = 0; i < this.LinesCount; i++)
            {
                if (this.lines.IsLineLoaded(i))
                {
                    if (this.lines[i].LastVisit > this.lastNavigatedDateTime && this.lines[i].LastVisit < min)
                    {
                        min = this.lines[i].LastVisit;
                        iLine = i;
                    }
                }
            }
            if (iLine >= 0)
            {
                this.Navigate(iLine);
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 	Navigates backward (by Line.LastVisit property)
        /// </summary>
        public bool NavigateBackward()
        {
            var max = new DateTime();
            int iLine = -1;
            for (int i = 0; i < this.LinesCount; i++)
            {
                if (this.lines.IsLineLoaded(i))
                {
                    if (this.lines[i].LastVisit < this.lastNavigatedDateTime && this.lines[i].LastVisit > max)
                    {
                        max = this.lines[i].LastVisit;
                        iLine = i;
                    }
                }
            }
            if (iLine >= 0)
            {
                this.Navigate(iLine);
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 	Navigates to defined line, without Line.LastVisit reseting
        /// </summary>
        public void Navigate(int iLine)
        {
            if (iLine >= this.LinesCount)
            {
                return;
            }
            this.lastNavigatedDateTime = this.lines[iLine].LastVisit;
            this.Selection.Start = new Place(0, iLine);
            this.DoSelectionVisible();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.m_hImc = ImmGetContext(this.Handle);
        }

        private void timer2_Tick(object sender, EventArgs e)
        {
            this.timer2.Enabled = false;
            if (this.needRiseTextChangedDelayed)
            {
                this.needRiseTextChangedDelayed = false;
                if (this.delayedTextChangedRange == null)
                {
                    return;
                }
                this.delayedTextChangedRange = this.Range.GetIntersectionWith(this.delayedTextChangedRange);
                this.delayedTextChangedRange.Expand();
                this.OnTextChangedDelayed(this.delayedTextChangedRange);
                this.delayedTextChangedRange = null;
            }
        }

        public void AddVisualMarker(VisualMarker marker)
        {
            this.visibleMarkers.Add(marker);
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            this.timer.Enabled = false;
            if (this.needRiseSelectionChangedDelayed)
            {
                this.needRiseSelectionChangedDelayed = false;
                this.OnSelectionChangedDelayed();
            }
            if (this.needRiseVisibleRangeChangedDelayed)
            {
                this.needRiseVisibleRangeChangedDelayed = false;
                this.OnVisibleRangeChangedDelayed();
            }
        }

        public virtual void OnTextChangedDelayed(Range changedRange)
        {
            if (this.TextChangedDelayed != null)
            {
                this.TextChangedDelayed(this, new TextChangedEventArgs(changedRange));
            }
        }

        public virtual void OnSelectionChangedDelayed()
        {
            this.RecalcScrollByOneLine(this.Selection.Start.iLine);
            //highlight brackets
            this.ClearBracketsPositions();
            if (this.LeftBracket != '\x0' && this.RightBracket != '\x0')
            {
                this.HighlightBrackets(
                    this.LeftBracket, this.RightBracket, ref this.leftBracketPosition, ref this.rightBracketPosition);
            }
            if (this.LeftBracket2 != '\x0' && this.RightBracket2 != '\x0')
            {
                this.HighlightBrackets(
                    this.LeftBracket2, this.RightBracket2, ref this.leftBracketPosition2, ref this.rightBracketPosition2);
            }
            //remember last visit time
            if (this.Selection.IsEmpty && this.Selection.Start.iLine < this.LinesCount)
            {
                if (this.lastNavigatedDateTime != this.lines[this.Selection.Start.iLine].LastVisit)
                {
                    this.lines[this.Selection.Start.iLine].LastVisit = DateTime.Now;
                    this.lastNavigatedDateTime = this.lines[this.Selection.Start.iLine].LastVisit;
                }
            }

            if (this.SelectionChangedDelayed != null)
            {
                this.SelectionChangedDelayed(this, new EventArgs());
            }
        }

        public virtual void OnVisibleRangeChangedDelayed()
        {
            if (this.VisibleRangeChangedDelayed != null)
            {
                this.VisibleRangeChangedDelayed(this, new EventArgs());
            }
        }

        private readonly Dictionary<Timer, Timer> timersToReset = new Dictionary<Timer, Timer>();

        private void ResetTimer(Timer timer)
        {
            timer.Stop();
            if (this.IsHandleCreated)
            {
                timer.Start();
            }
            else
            {
                this.timersToReset[timer] = timer;
            }
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            foreach (var timer in new List<Timer>(this.timersToReset.Keys))
            {
                this.ResetTimer(timer);
            }
            this.timersToReset.Clear();

            this.OnScrollbarsUpdated();
        }

        /// <summary>
        /// 	Add new style
        /// </summary>
        /// <returns> Layer index of this style </returns>
        public int AddStyle(Style style)
        {
            if (style == null)
            {
                return -1;
            }

            int i = this.GetStyleIndex(style);
            if (i >= 0)
            {
                return i;
            }

            for (i = this.Styles.Length - 1; i >= 0; i--)
            {
                if (this.Styles[i] != null)
                {
                    break;
                }
            }

            i++;
            if (i >= this.Styles.Length)
            {
                throw new Exception("Maximum count of Styles is exceeded.");
            }

            this.Styles[i] = style;
            return i;
        }

        /// <summary>
        /// 	Shows find dialog
        /// </summary>
        public void ShowFindDialog()
        {
            this.ShowFindDialog(null);
        }

        /// <summary>
        /// 	Shows find dialog
        /// </summary>
        public void ShowFindDialog(string findText)
        {
            if (this.findForm == null)
            {
                this.findForm = new FindForm(this);
            }

            if (findText != null)
            {
                this.findForm.tbFind.Text = findText;
            }
            else if (!this.Selection.IsEmpty && this.Selection.Start.iLine == this.Selection.End.iLine)
            {
                this.findForm.tbFind.Text = this.Selection.Text;
            }

            this.findForm.tbFind.SelectAll();
            this.findForm.Show();
        }

        /// <summary>
        /// 	Shows replace dialog
        /// </summary>
        public void ShowReplaceDialog()
        {
            this.ShowReplaceDialog(null);
        }

        /// <summary>
        /// 	Shows replace dialog
        /// </summary>
        public void ShowReplaceDialog(string findText)
        {
            if (this.ReadOnly)
            {
                return;
            }
            if (this.replaceForm == null)
            {
                this.replaceForm = new ReplaceForm(this);
            }

            if (findText != null)
            {
                this.replaceForm.tbFind.Text = findText;
            }
            else if (!this.Selection.IsEmpty && this.Selection.Start.iLine == this.Selection.End.iLine)
            {
                this.replaceForm.tbFind.Text = this.Selection.Text;
            }

            this.replaceForm.tbFind.SelectAll();
            this.replaceForm.Show();
        }

        /// <summary>
        /// 	Gets length of given line
        /// </summary>
        /// <param name="iLine"> Line index </param>
        /// <returns> Length of line </returns>
        public int GetLineLength(int iLine)
        {
            if (iLine < 0 || iLine >= this.lines.Count)
            {
                throw new ArgumentOutOfRangeException("Line index out of range");
            }

            return this.lines[iLine].Count;
        }

        /// <summary>
        /// 	Get range of line
        /// </summary>
        /// <param name="iLine"> Line index </param>
        public Range GetLine(int iLine)
        {
            if (iLine < 0 || iLine >= this.lines.Count)
            {
                throw new ArgumentOutOfRangeException("Line index out of range");
            }

            var sel = new Range(this);
            sel.Start = new Place(0, iLine);
            sel.End = new Place(this.lines[iLine].Count, iLine);
            return sel;
        }

        /// <summary>
        /// 	Copy selected text into Clipboard
        /// </summary>
        public virtual void Copy()
        {
            if (this.Selection.IsEmpty)
            {
                this.Selection.Expand();
            }
            if (!this.Selection.IsEmpty)
            {
                var data = new DataObject();
                this.OnCreateClipboardData(data);
                //
                var thread = new Thread(() => this.SetClipboard(data));
                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
                thread.Join();
            }
        }

        protected virtual void OnCreateClipboardData(DataObject data)
        {
            var exp = new ExportToHTML();
            exp.UseBr = false;
            exp.UseNbsp = false;
            exp.UseStyleTag = true;
            string html = "<pre>" + exp.GetHtml(this.Selection.Clone()) + "</pre>";

            data.SetData(DataFormats.UnicodeText, true, this.Selection.Text);
            data.SetData(DataFormats.Html, PrepareHtmlForClipboard(html));
            data.SetData(DataFormats.Rtf, new ExportToRTF().GetRtf(this.Selection.Clone()));
        }

        [DllImport("user32.dll")]
        private static extern IntPtr GetOpenClipboardWindow();

        [DllImport("user32.dll")]
        private static extern IntPtr CloseClipboard();

        protected void SetClipboard(DataObject data)
        {
            try
            {
                /*
                    while (GetOpenClipboardWindow() != IntPtr.Zero)
                        Thread.Sleep(0);*/
                CloseClipboard();
                Clipboard.SetDataObject(data, true, 5, 100);
            }
            catch (ExternalException)
            {
                //occurs if some other process holds open clipboard
            }
        }

        public static MemoryStream PrepareHtmlForClipboard(string html)
        {
            Encoding enc = Encoding.UTF8;

            string begin = "Version:0.9\r\nStartHTML:{0:000000}\r\nEndHTML:{1:000000}"
                           + "\r\nStartFragment:{2:000000}\r\nEndFragment:{3:000000}\r\n";

            string html_begin = "<html>\r\n<head>\r\n" + "<meta http-equiv=\"Content-Type\""
                                + " content=\"text/html; charset=" + enc.WebName + "\">\r\n"
                                + "<title>HTML clipboard</title>\r\n</head>\r\n<body>\r\n" + "<!--StartFragment-->";

            string html_end = "<!--EndFragment-->\r\n</body>\r\n</html>\r\n";

            string begin_sample = String.Format(begin, 0, 0, 0, 0);

            int count_begin = enc.GetByteCount(begin_sample);
            int count_html_begin = enc.GetByteCount(html_begin);
            int count_html = enc.GetByteCount(html);
            int count_html_end = enc.GetByteCount(html_end);

            string html_total = String.Format(
                begin,
                count_begin,
                count_begin + count_html_begin + count_html + count_html_end,
                count_begin + count_html_begin,
                count_begin + count_html_begin + count_html) + html_begin + html + html_end;

            return new MemoryStream(enc.GetBytes(html_total));
        }

        /// <summary>
        /// 	Cut selected text into Clipboard
        /// </summary>
        public virtual void Cut()
        {
            if (!this.Selection.IsEmpty)
            {
                this.Copy();
                this.ClearSelected();
            }
            else if (this.LinesCount == 1)
            {
                this.Selection.SelectAll();
                this.Copy();
                this.ClearSelected();
            }
            else
            {
                this.Copy();
                //remove current line
                if (this.Selection.Start.iLine >= 0 && this.Selection.Start.iLine < this.LinesCount)
                {
                    int iLine = this.Selection.Start.iLine;
                    this.RemoveLines(new List<int> { iLine });
                    this.Selection.Start = new Place(0, Math.Max(0, Math.Min(iLine, this.LinesCount - 1)));
                }
            }
        }

        /// <summary>
        /// 	Paste text from clipboard into selected position
        /// </summary>
        public virtual void Paste()
        {
            string text = null;
            var thread = new Thread(
                () =>
                    {
                        if (Clipboard.ContainsText())
                        {
                            text = Clipboard.GetText();
                        }
                    });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();

            if (this.Pasting != null)
            {
                var args = new TextChangingEventArgs { Cancel = false, InsertingText = text };

                this.Pasting(this, args);

                if (args.Cancel)
                {
                    text = string.Empty;
                }
                else
                {
                    text = args.InsertingText;
                }
            }

            if (!string.IsNullOrEmpty(text))
            {
                this.InsertText(text);
            }
        }

        /// <summary>
        /// 	Select all chars of text
        /// </summary>
        public void SelectAll()
        {
            this.Selection.SelectAll();
        }

        /// <summary>
        /// 	Move caret to end of text
        /// </summary>
        public void GoEnd()
        {
            if (this.lines.Count > 0)
            {
                this.Selection.Start = new Place(this.lines[this.lines.Count - 1].Count, this.lines.Count - 1);
            }
            else
            {
                this.Selection.Start = new Place(0, 0);
            }

            this.DoCaretVisible();
        }

        /// <summary>
        /// 	Move caret to first position
        /// </summary>
        public void GoHome()
        {
            this.Selection.Start = new Place(0, 0);

            this.DoCaretVisible();
            //VerticalScroll.Value = 0;
            //HorizontalScroll.Value = 0;
        }

        /// <summary>
        /// 	Clear text, styles, history, caches
        /// </summary>
        public void Clear()
        {
            this.Selection.BeginUpdate();
            try
            {
                this.Selection.SelectAll();
                this.ClearSelected();
                this.lines.Manager.ClearHistory();
                this.Invalidate();
            }
            finally
            {
                this.Selection.EndUpdate();
            }
        }

        /// <summary>
        /// 	Clear buffer of styles
        /// </summary>
        public void ClearStylesBuffer()
        {
            for (int i = 0; i < this.Styles.Length; i++)
            {
                this.Styles[i] = null;
            }
        }

        /// <summary>
        /// 	Clear style of all text
        /// </summary>
        public void ClearStyle(StyleIndex styleIndex)
        {
            foreach (var line in this.lines)
            {
                line.ClearStyle(styleIndex);
            }

            for (int i = 0; i < this.LineInfos.Count; i++)
            {
                this.SetVisibleState(i, VisibleState.Visible);
            }

            this.Invalidate();
        }

        /// <summary>
        /// 	Clears undo and redo stacks
        /// </summary>
        public void ClearUndo()
        {
            this.lines.Manager.ClearHistory();
        }

        /// <summary>
        /// 	Insert text into current selected position
        /// </summary>
        public virtual void InsertText(string text)
        {
            this.InsertText(text, true);
        }

        /// <summary>
        /// 	Insert text into current selected position
        /// </summary>
        /// <param name="text"> </param>
        public virtual void InsertText(string text, bool jumpToCaret)
        {
            if (text == null)
            {
                return;
            }
            if (text == "\r")
            {
                text = "\n";
            }

            this.lines.Manager.BeginAutoUndoCommands();
            try
            {
                if (!this.Selection.IsEmpty)
                {
                    this.lines.Manager.ExecuteCommand(new ClearSelectedCommand(this.TextSource));
                }

                //insert virtual spaces
                if (this.TextSource.Count > 0)
                {
                    if (this.Selection.IsEmpty
                        && this.Selection.Start.iChar > this.GetLineLength(this.Selection.Start.iLine)
                        && this.VirtualSpace)
                    {
                        this.InsertVirtualSpaces();
                    }
                }

                this.lines.Manager.ExecuteCommand(new InsertTextCommand(this.TextSource, text));
                if (this.updating <= 0 && jumpToCaret)
                {
                    this.DoCaretVisible();
                }
            }
            finally
            {
                this.lines.Manager.EndAutoUndoCommands();
            }
            //
            this.Invalidate();
        }

        /// <summary>
        /// 	Insert text into current selection position (with predefined style)
        /// </summary>
        /// <param name="text"> </param>
        public virtual Range InsertText(string text, Style style)
        {
            return this.InsertText(text, style, true);
        }

        /// <summary>
        /// 	Insert text into current selection position (with predefined style)
        /// </summary>
        public virtual Range InsertText(string text, Style style, bool jumpToCaret)
        {
            if (text == null)
            {
                return null;
            }

            //remember last caret position
            Place last = this.Selection.Start > this.Selection.End ? this.Selection.End : this.Selection.Start;
            //insert text
            InsertText(text, jumpToCaret);
            //get range
            var range = new Range(this, last, this.Selection.Start)
                { ColumnSelectionMode = this.Selection.ColumnSelectionMode };
            //set style for range
            range.SetStyle(style);

            return range;
        }

        /// <summary>
        /// 	Append string to end of the Text
        /// </summary>
        public virtual void AppendText(string text)
        {
            this.AppendText(text, null);
        }

        /// <summary>
        /// 	Append string to end of the Text
        /// </summary>
        public virtual void AppendText(string text, Style style)
        {
            if (text == null)
            {
                return;
            }

            this.Selection.ColumnSelectionMode = false;

            Place oldStart = this.Selection.Start;
            Place oldEnd = this.Selection.End;

            this.Selection.BeginUpdate();
            this.lines.Manager.BeginAutoUndoCommands();
            try
            {
                if (this.lines.Count > 0)
                {
                    this.Selection.Start = new Place(this.lines[this.lines.Count - 1].Count, this.lines.Count - 1);
                }
                else
                {
                    this.Selection.Start = new Place(0, 0);
                }

                //remember last caret position
                Place last = this.Selection.Start;

                this.lines.Manager.ExecuteCommand(new InsertTextCommand(this.TextSource, text));

                if (style != null)
                {
                    new Range(this, last, this.Selection.Start).SetStyle(style);
                }
            }
            finally
            {
                this.lines.Manager.EndAutoUndoCommands();
                this.Selection.Start = oldStart;
                this.Selection.End = oldEnd;
                this.Selection.EndUpdate();
            }
            //
            this.Invalidate();
        }

        /// <summary>
        /// 	Returns index of the style in Styles
        /// 	-1 otherwise
        /// </summary>
        /// <param name="style"> </param>
        /// <returns> Index of the style in Styles </returns>
        public int GetStyleIndex(Style style)
        {
            return Array.IndexOf(this.Styles, style);
        }

        /// <summary>
        /// 	Returns StyleIndex mask of given styles
        /// </summary>
        /// <param name="styles"> </param>
        /// <returns> StyleIndex mask of given styles </returns>
        public StyleIndex GetStyleIndexMask(Style[] styles)
        {
            var mask = StyleIndex.None;
            foreach (var style in styles)
            {
                int i = this.GetStyleIndex(style);
                if (i >= 0)
                {
                    mask |= Range.ToStyleIndex(i);
                }
            }

            return mask;
        }

        internal int GetOrSetStyleLayerIndex(Style style)
        {
            int i = this.GetStyleIndex(style);
            if (i < 0)
            {
                i = this.AddStyle(style);
            }
            return i;
        }

        public static SizeF GetCharSize(Font font, char c)
        {
            Size sz2 = TextRenderer.MeasureText("<" + c.ToString() + ">", font);
            Size sz3 = TextRenderer.MeasureText("<>", font);

            return new SizeF(sz2.Width - sz3.Width + 1, /*sz2.Height*/font.Height);
        }

        [DllImport("Imm32.dll")]
        public static extern IntPtr ImmGetContext(IntPtr hWnd);

        [DllImport("Imm32.dll")]
        public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
            {
                if (m.WParam.ToInt32() != SB_ENDSCROLL)
                {
                    this.Invalidate();
                }
            }

            base.WndProc(ref m);

            if (this.ImeAllowed)
            {
                if (m.Msg == WM_IME_SETCONTEXT && m.WParam.ToInt32() == 1)
                {
                    ImmAssociateContext(this.Handle, this.m_hImc);
                }
            }
        }

        public void OnScroll(ScrollEventArgs se, bool alignByLines)
        {
            if (se.ScrollOrientation == ScrollOrientation.VerticalScroll)
            {
                //align by line height
                int newValue = se.NewValue;
                if (alignByLines)
                {
                    newValue = (int)(Math.Ceiling(1d * newValue / this.CharHeight) * this.CharHeight);
                }
                //
                this.VerticalScroll.Value = Math.Max(
                    this.VerticalScroll.Minimum, Math.Min(this.VerticalScroll.Maximum, newValue));
            }
            if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll)
            {
                this.HorizontalScroll.Value = Math.Max(
                    this.HorizontalScroll.Minimum, Math.Min(this.HorizontalScroll.Maximum, se.NewValue));
            }

            this.UpdateScrollbars();

            this.Invalidate();
            //
            base.OnScroll(se);
            this.OnVisibleRangeChanged();
        }

        protected override void OnScroll(ScrollEventArgs se)
        {
            this.OnScroll(se, true);
        }

        protected virtual void InsertChar(char c)
        {
            this.lines.Manager.BeginAutoUndoCommands();
            try
            {
                if (!this.Selection.IsEmpty)
                {
                    this.lines.Manager.ExecuteCommand(new ClearSelectedCommand(this.TextSource));
                }

                //insert virtual spaces
                if (this.Selection.IsEmpty
                    && this.Selection.Start.iChar > this.GetLineLength(this.Selection.Start.iLine) && this.VirtualSpace)
                {
                    this.InsertVirtualSpaces();
                }

                //insert char
                this.lines.Manager.ExecuteCommand(new InsertCharCommand(this.TextSource, c));
            }
            finally
            {
                this.lines.Manager.EndAutoUndoCommands();
            }

            this.Invalidate();
        }

        private void InsertVirtualSpaces()
        {
            int lineLength = this.GetLineLength(this.Selection.Start.iLine);
            int count = this.Selection.Start.iChar - lineLength;
            this.Selection.BeginUpdate();
            try
            {
                this.Selection.Start = new Place(lineLength, this.Selection.Start.iLine);
                this.lines.Manager.ExecuteCommand(new InsertTextCommand(this.TextSource, new string(' ', count)));
            }
            finally
            {
                this.Selection.EndUpdate();
            }
        }

        /// <summary>
        /// 	Deletes selected chars
        /// </summary>
        public virtual void ClearSelected()
        {
            if (!this.Selection.IsEmpty)
            {
                this.lines.Manager.ExecuteCommand(new ClearSelectedCommand(this.TextSource));
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Deletes current line(s)
        /// </summary>
        public void ClearCurrentLine()
        {
            this.Selection.Expand();

            this.lines.Manager.ExecuteCommand(new ClearSelectedCommand(this.TextSource));
            if (this.Selection.Start.iLine == 0)
            {
                if (!this.Selection.GoRightThroughFolded())
                {
                    return;
                }
            }
            if (this.Selection.Start.iLine > 0)
            {
                this.lines.Manager.ExecuteCommand(new InsertCharCommand(this.TextSource, '\b')); //backspace
            }
            this.Invalidate();
        }

        private void Recalc()
        {
            if (!this.needRecalc)
            {
                return;
            }

#if debug
            var sw = Stopwatch.StartNew();
#endif

            this.needRecalc = false;
            //calc min left indent
            this.LeftIndent = this.LeftPadding;
            long maxLineNumber = this.LinesCount + this.lineNumberStartValue - 1;
            int charsForLineNumber = 2 + (maxLineNumber > 0 ? (int)Math.Log10(maxLineNumber) : 0);

            // If there are reserved character for line numbers: correct this
            if (this.ReservedCountOfLineNumberChars + 1 > charsForLineNumber)
            {
                charsForLineNumber = this.ReservedCountOfLineNumberChars + 1;
            }

            if (this.Created)
            {
                if (this.ShowLineNumbers)
                {
                    this.LeftIndent += charsForLineNumber * this.CharWidth + minLeftIndent + 1;
                }

                //calc wordwrapping
                if (this.needRecalcWordWrap)
                {
                    this.RecalcWordWrap(this.needRecalcWordWrapInterval.X, this.needRecalcWordWrapInterval.Y);
                    this.needRecalcWordWrap = false;
                }
            }
            else
            {
                this.needRecalc = true;
            }

            //calc max line length and count of wordWrapLines
            this.TextHeight = 0;

            this.maxLineLength = this.RecalcMaxLineLength();

            //adjust AutoScrollMinSize
            int minWidth;
            this.CalcMinAutosizeWidth(out minWidth, ref this.maxLineLength);

            this.AutoScrollMinSize = new Size(minWidth, this.TextHeight + this.Paddings.Top + this.Paddings.Bottom);
            this.UpdateScrollbars();
#if debug
            sw.Stop();
            Console.WriteLine("Recalc: " + sw.ElapsedMilliseconds);
#endif
        }

        private void CalcMinAutosizeWidth(out int minWidth, ref int maxLineLength)
        {
            //adjust AutoScrollMinSize
            minWidth = this.LeftIndent + (maxLineLength) * this.CharWidth + 2 + this.Paddings.Left + this.Paddings.Right;
            if (this.wordWrap)
            {
                switch (this.WordWrapMode)
                {
                    case WordWrapMode.WordWrapControlWidth:
                    case WordWrapMode.CharWrapControlWidth:
                        maxLineLength = Math.Min(
                            maxLineLength,
                            (this.ClientSize.Width - this.LeftIndent - this.Paddings.Left - this.Paddings.Right)
                            / this.CharWidth);
                        minWidth = 0;
                        break;
                    case WordWrapMode.WordWrapPreferredWidth:
                    case WordWrapMode.CharWrapPreferredWidth:
                        maxLineLength = Math.Min(maxLineLength, this.PreferredLineWidth);
                        minWidth = this.LeftIndent + this.PreferredLineWidth * this.CharWidth + 2 + this.Paddings.Left
                                   + this.Paddings.Right;
                        break;
                }
            }
        }

        private void RecalcScrollByOneLine(int iLine)
        {
            if (iLine >= this.lines.Count)
            {
                return;
            }

            int maxLineLength = this.lines[iLine].Count;
            if (this.maxLineLength < maxLineLength && !this.WordWrap)
            {
                this.maxLineLength = maxLineLength;
            }

            int minWidth;
            this.CalcMinAutosizeWidth(out minWidth, ref maxLineLength);

            if (this.AutoScrollMinSize.Width < minWidth)
            {
                this.AutoScrollMinSize = new Size(minWidth, this.AutoScrollMinSize.Height);
            }
        }

        private int RecalcMaxLineLength()
        {
            int maxLineLength = 0;
            TextSource lines = this.lines;
            int count = lines.Count;
            int charHeight = this.CharHeight;
            int topIndent = this.Paddings.Top;
            this.TextHeight = topIndent;

            for (int i = 0; i < count; i++)
            {
                int lineLength = lines.GetLineLength(i);
                LineInfo lineInfo = this.LineInfos[i];
                if (lineLength > maxLineLength && lineInfo.VisibleState == VisibleState.Visible)
                {
                    maxLineLength = lineLength;
                }
                lineInfo.startY = this.TextHeight;
                this.TextHeight += lineInfo.WordWrapStringsCount * charHeight + lineInfo.bottomPadding;
                this.LineInfos[i] = lineInfo;
            }

            this.TextHeight -= topIndent;

            return maxLineLength;
        }

        private int GetMaxLineWordWrapedWidth()
        {
            if (this.wordWrap)
            {
                switch (this.wordWrapMode)
                {
                    case WordWrapMode.WordWrapControlWidth:
                    case WordWrapMode.CharWrapControlWidth:
                        return this.ClientSize.Width;
                    case WordWrapMode.WordWrapPreferredWidth:
                    case WordWrapMode.CharWrapPreferredWidth:
                        return this.LeftIndent + this.PreferredLineWidth * this.CharWidth + 2 + this.Paddings.Left
                               + this.Paddings.Right;
                }
            }

            return int.MaxValue;
        }

        private void RecalcWordWrap(int fromLine, int toLine)
        {
            int maxCharsPerLine = 0;
            bool charWrap = false;

            toLine = Math.Min(this.LinesCount - 1, toLine);

            switch (this.WordWrapMode)
            {
                case WordWrapMode.WordWrapControlWidth:
                    maxCharsPerLine = (this.ClientSize.Width - this.LeftIndent - this.Paddings.Left
                                       - this.Paddings.Right) / this.CharWidth;
                    break;
                case WordWrapMode.CharWrapControlWidth:
                    maxCharsPerLine = (this.ClientSize.Width - this.LeftIndent - this.Paddings.Left
                                       - this.Paddings.Right) / this.CharWidth;
                    charWrap = true;
                    break;
                case WordWrapMode.WordWrapPreferredWidth:
                    maxCharsPerLine = this.PreferredLineWidth;
                    break;
                case WordWrapMode.CharWrapPreferredWidth:
                    maxCharsPerLine = this.PreferredLineWidth;
                    charWrap = true;
                    break;
            }

            for (int iLine = fromLine; iLine <= toLine; iLine++)
            {
                if (this.lines.IsLineLoaded(iLine))
                {
                    if (!this.wordWrap)
                    {
                        this.LineInfos[iLine].CutOffPositions.Clear();
                    }
                    else
                    {
                        LineInfo li = this.LineInfos[iLine];

                        li.wordWrapIndent = this.WordWrapAutoIndent
                                                ? this.lines[iLine].StartSpacesCount + this.WordWrapIndent
                                                : this.WordWrapIndent;

                        if (this.WordWrapMode == WordWrapMode.Custom)
                        {
                            if (this.WordWrapNeeded != null)
                            {
                                this.WordWrapNeeded(
                                    this,
                                    new WordWrapNeededEventArgs(li.CutOffPositions, this.ImeAllowed, this.lines[iLine]));
                            }
                        }
                        else
                        {
                            CalcCutOffs(
                                li.CutOffPositions,
                                maxCharsPerLine,
                                maxCharsPerLine - li.wordWrapIndent,
                                this.ImeAllowed,
                                charWrap,
                                this.lines[iLine]);
                        }

                        this.LineInfos[iLine] = li;
                    }
                }
            }
            this.needRecalc = true;
        }

        /// <summary>
        /// 	Calculates wordwrap cutoffs
        /// </summary>
        public static void CalcCutOffs(
            List<int> cutOffPositions,
            int maxCharsPerLine,
            int maxCharsPerSecondaryLine,
            bool allowIME,
            bool charWrap,
            Line line)
        {
            if (maxCharsPerSecondaryLine < 1)
            {
                maxCharsPerSecondaryLine = 1;
            }
            if (maxCharsPerLine < 1)
            {
                maxCharsPerLine = 1;
            }

            int segmentLength = 0;
            int cutOff = 0;
            cutOffPositions.Clear();

            for (int i = 0; i < line.Count - 1; i++)
            {
                char c = line[i].c;
                if (charWrap)
                {
                    //char wrapping
                    cutOff = i + 1;
                }
                else
                {
                    //word wrapping
                    if (allowIME && IsCJKLetter(c)) //in CJK languages cutoff can be in any letter
                    {
                        cutOff = i;
                    }
                    else if (!char.IsLetterOrDigit(c) && c != '_' && c != '\'')
                    {
                        cutOff = Math.Min(i + 1, line.Count - 1);
                    }
                }

                segmentLength++;

                if (segmentLength == maxCharsPerLine)
                {
                    if (cutOff == 0
                        || (cutOffPositions.Count > 0 && cutOff == cutOffPositions[cutOffPositions.Count - 1]))
                    {
                        cutOff = i + 1;
                    }
                    cutOffPositions.Add(cutOff);
                    segmentLength = 1 + i - cutOff;
                    maxCharsPerLine = maxCharsPerSecondaryLine;
                }
            }
        }

        public static bool IsCJKLetter(char c)
        {
            int code = Convert.ToInt32(c);
            return (code >= 0x3300 && code <= 0x33FF) || (code >= 0xFE30 && code <= 0xFE4F)
                   || (code >= 0xF900 && code <= 0xFAFF) || (code >= 0x2E80 && code <= 0x2EFF)
                   || (code >= 0x31C0 && code <= 0x31EF) || (code >= 0x4E00 && code <= 0x9FFF)
                   || (code >= 0x3400 && code <= 0x4DBF) || (code >= 0x3200 && code <= 0x32FF)
                   || (code >= 0x2460 && code <= 0x24FF) || (code >= 0x3040 && code <= 0x309F)
                   || (code >= 0x2F00 && code <= 0x2FDF) || (code >= 0x31A0 && code <= 0x31BF)
                   || (code >= 0x4DC0 && code <= 0x4DFF) || (code >= 0x3100 && code <= 0x312F)
                   || (code >= 0x30A0 && code <= 0x30FF) || (code >= 0x31F0 && code <= 0x31FF)
                   || (code >= 0x2FF0 && code <= 0x2FFF) || (code >= 0x1100 && code <= 0x11FF)
                   || (code >= 0xA960 && code <= 0xA97F) || (code >= 0xD7B0 && code <= 0xD7FF)
                   || (code >= 0x3130 && code <= 0x318F) || (code >= 0xAC00 && code <= 0xD7AF);
        }

        protected override void OnClientSizeChanged(EventArgs e)
        {
            base.OnClientSizeChanged(e);
            if (this.WordWrap)
            {
                //RecalcWordWrap(0, lines.Count - 1);
                this.NeedRecalc(false, true);
                this.Invalidate();
            }
            this.OnVisibleRangeChanged();
            this.UpdateScrollbars();
        }

        /// <summary>
        /// 	Scroll control for display defined rectangle
        /// </summary>
        /// <param name="rect"> </param>
        private void DoVisibleRectangle(Rectangle rect)
        {
            int oldV = this.VerticalScroll.Value;
            int v = this.VerticalScroll.Value;
            int h = this.HorizontalScroll.Value;

            if (rect.Bottom > this.ClientRectangle.Height)
            {
                v += rect.Bottom - this.ClientRectangle.Height;
            }
            else if (rect.Top < 0)
            {
                v += rect.Top;
            }

            if (rect.Right > this.ClientRectangle.Width)
            {
                h += rect.Right - this.ClientRectangle.Width;
            }
            else if (rect.Left < this.LeftIndent)
            {
                h += rect.Left - this.LeftIndent;
            }
            //
            if (!this.Multiline)
            {
                v = 0;
            }
            //
            v = Math.Max(this.VerticalScroll.Minimum, v); // was 0
            h = Math.Max(this.HorizontalScroll.Minimum, h); // was 0
            //
            try
            {
                if (this.VerticalScroll.Visible || !this.ShowScrollBars)
                {
                    this.VerticalScroll.Value = Math.Min(v, this.VerticalScroll.Maximum);
                }
                if (this.HorizontalScroll.Visible || !this.ShowScrollBars)
                {
                    this.HorizontalScroll.Value = Math.Min(h, this.HorizontalScroll.Maximum);
                }
            }
            catch (ArgumentOutOfRangeException)
            {
                ;
            }

            this.UpdateScrollbars();
            //
            if (oldV != this.VerticalScroll.Value)
            {
                this.OnVisibleRangeChanged();
            }
        }

        /// <summary>
        /// 	Updates scrollbar position after Value changed
        /// </summary>
        public void UpdateScrollbars()
        {
            if (this.ShowScrollBars)
            {
                //some magic for update scrolls
                base.AutoScrollMinSize -= new Size(1, 0);
                base.AutoScrollMinSize += new Size(1, 0);
            }
            else
            {
                this.AutoScrollMinSize = this.AutoScrollMinSize;
            }

            if (this.IsHandleCreated)
            {
                this.BeginInvoke((MethodInvoker)this.OnScrollbarsUpdated);
            }
        }

        protected virtual void OnScrollbarsUpdated()
        {
            if (this.ScrollbarsUpdated != null)
            {
                this.ScrollbarsUpdated(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 	Scroll control for display caret
        /// </summary>
        public void DoCaretVisible()
        {
            this.Invalidate();
            this.Recalc();
            Point car = this.PlaceToPoint(this.Selection.Start);
            car.Offset(-this.CharWidth, 0);
            this.DoVisibleRectangle(new Rectangle(car, new Size(2 * this.CharWidth, 2 * this.CharHeight)));
        }

        /// <summary>
        /// 	Scroll control left
        /// </summary>
        public void ScrollLeft()
        {
            this.Invalidate();
            this.HorizontalScroll.Value = 0;
            this.AutoScrollMinSize -= new Size(1, 0);
            this.AutoScrollMinSize += new Size(1, 0);
        }

        /// <summary>
        /// 	Scroll control for display selection area
        /// </summary>
        public void DoSelectionVisible()
        {
            if (this.LineInfos[this.Selection.End.iLine].VisibleState != VisibleState.Visible)
            {
                this.ExpandBlock(this.Selection.End.iLine);
            }

            if (this.LineInfos[this.Selection.Start.iLine].VisibleState != VisibleState.Visible)
            {
                this.ExpandBlock(this.Selection.Start.iLine);
            }

            this.Recalc();
            this.DoVisibleRectangle(
                new Rectangle(
                    this.PlaceToPoint(new Place(0, this.Selection.End.iLine)),
                    new Size(2 * this.CharWidth, 2 * this.CharHeight)));

            Point car = this.PlaceToPoint(this.Selection.Start);
            Point car2 = this.PlaceToPoint(this.Selection.End);
            car.Offset(-this.CharWidth, -this.ClientSize.Height / 2);
            this.DoVisibleRectangle(new Rectangle(car, new Size(Math.Abs(car2.X - car.X), this.ClientSize.Height)));
            //Math.Abs(car2.Y-car.Y) + 2 * CharHeight

            this.Invalidate();
        }

        /// <summary>
        /// 	Scroll control for display given range
        /// </summary>
        public void DoRangeVisible(Range range)
        {
            this.DoRangeVisible(range, false);
        }

        /// <summary>
        /// 	Scroll control for display given range
        /// </summary>
        public void DoRangeVisible(Range range, bool tryToCentre)
        {
            range = range.Clone();
            range.Normalize();
            range.End = new Place(
                range.End.iChar, Math.Min(range.End.iLine, range.Start.iLine + this.ClientSize.Height / this.CharHeight));

            if (this.LineInfos[range.End.iLine].VisibleState != VisibleState.Visible)
            {
                this.ExpandBlock(range.End.iLine);
            }

            if (this.LineInfos[range.Start.iLine].VisibleState != VisibleState.Visible)
            {
                this.ExpandBlock(range.Start.iLine);
            }

            this.Recalc();
            int h = (1 + range.End.iLine - range.Start.iLine) * this.CharHeight;
            Point p = this.PlaceToPoint(new Place(0, range.Start.iLine));
            if (tryToCentre)
            {
                p.Offset(0, -this.ClientSize.Height / 2);
                h = this.ClientSize.Height;
            }
            this.DoVisibleRectangle(new Rectangle(p, new Size(2 * this.CharWidth, h)));

            this.Invalidate();
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            if (e.KeyCode == Keys.ShiftKey)
            {
                this.lastModifiers &= ~Keys.Shift;
            }
            if (e.KeyCode == Keys.Alt)
            {
                this.lastModifiers &= ~Keys.Alt;
            }
            if (e.KeyCode == Keys.ControlKey)
            {
                this.lastModifiers &= ~Keys.Control;
            }
        }

        private bool findCharMode;

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (this.middleClickScrollingActivated)
            {
                return;
            }

            base.OnKeyDown(e);

            if (this.Focused) //??? 
            {
                this.lastModifiers = e.Modifiers;
            }

            this.handledChar = false;

            if (e.Handled)
            {
                this.handledChar = true;
                return;
            }

            if (this.ProcessKey(e.KeyData))
            {
                return;
            }

            e.Handled = true;

            this.DoCaretVisible();
            this.Invalidate();
        }

        protected override bool ProcessDialogKey(Keys keyData)
        {
            if ((keyData & Keys.Alt) > 0)
            {
                if (this.HotkeysMapping.ContainsKey(keyData))
                {
                    this.ProcessKey(keyData);
                    return true;
                }
            }

            return base.ProcessDialogKey(keyData);
        }

        private static readonly Dictionary<FCTBAction, bool> scrollActions = new Dictionary<FCTBAction, bool>
            {
                { FCTBAction.ScrollDown, true },
                { FCTBAction.ScrollUp, true },
                { FCTBAction.ZoomOut, true },
                { FCTBAction.ZoomIn, true },
                { FCTBAction.ZoomNormal, true }
            };

        /// <summary>
        /// 	Process control keys
        /// </summary>
        public virtual bool ProcessKey(Keys keyData)
        {
            var a = new KeyEventArgs(keyData);

            if (a.KeyCode == Keys.Tab && !this.AcceptsTab)
            {
                return false;
            }

            if (this.macrosManager != null)
            {
                if (!this.HotkeysMapping.ContainsKey(keyData)
                    ||
                    (this.HotkeysMapping[keyData] != FCTBAction.MacroExecute
                     && this.HotkeysMapping[keyData] != FCTBAction.MacroRecord))
                {
                    this.macrosManager.ProcessKey(keyData);
                }
            }

            if (this.HotkeysMapping.ContainsKey(keyData))
            {
                FCTBAction act = this.HotkeysMapping[keyData];
                this.DoAction(act);
                if (scrollActions.ContainsKey(act))
                {
                    return true;
                }
                if (keyData == Keys.Tab || keyData == (Keys.Tab | Keys.Shift))
                {
                    this.handledChar = true;
                    return true;
                }
            }
            else
            {
                //
                if (a.KeyCode == Keys.Alt)
                {
                    return true;
                }

                if ((a.Modifiers & Keys.Control) != 0)
                {
                    return true;
                }

                if ((a.Modifiers & Keys.Alt) != 0)
                {
                    if ((MouseButtons & MouseButtons.Left) != 0)
                    {
                        this.CheckAndChangeSelectionType();
                    }
                    return true;
                }

                if (a.KeyCode == Keys.ShiftKey)
                {
                    return true;
                }
            }

            return false;
        }

        private void DoAction(FCTBAction action)
        {
            switch (action)
            {
                case FCTBAction.ZoomIn:
                    this.ChangeFontSize(2);
                    break;
                case FCTBAction.ZoomOut:
                    this.ChangeFontSize(-2);
                    break;
                case FCTBAction.ZoomNormal:
                    this.RestoreFontSize();
                    break;
                case FCTBAction.ScrollDown:
                    this.DoScrollVertical(1, -1);
                    break;

                case FCTBAction.ScrollUp:
                    this.DoScrollVertical(1, 1);
                    break;

                case FCTBAction.GoToDialog:
                    this.ShowGoToDialog();
                    break;

                case FCTBAction.FindDialog:
                    this.ShowFindDialog();
                    break;

                case FCTBAction.FindChar:
                    this.findCharMode = true;
                    break;

                case FCTBAction.FindNext:
                    if (this.findForm == null || this.findForm.tbFind.Text == "")
                    {
                        this.ShowFindDialog();
                    }
                    else
                    {
                        this.findForm.FindNext(this.findForm.tbFind.Text);
                    }
                    break;

                case FCTBAction.ReplaceDialog:
                    this.ShowReplaceDialog();
                    break;

                case FCTBAction.Copy:
                    this.Copy();
                    break;

                case FCTBAction.CommentSelected:
                    this.CommentSelected();
                    break;

                case FCTBAction.Cut:
                    if (!this.Selection.ReadOnly)
                    {
                        this.Cut();
                    }
                    break;

                case FCTBAction.Paste:
                    if (!this.Selection.ReadOnly)
                    {
                        this.Paste();
                    }
                    break;

                case FCTBAction.SelectAll:
                    this.Selection.SelectAll();
                    break;

                case FCTBAction.Undo:
                    if (!this.ReadOnly)
                    {
                        this.Undo();
                    }
                    break;

                case FCTBAction.Redo:
                    if (!this.ReadOnly)
                    {
                        this.Redo();
                    }
                    break;

                case FCTBAction.LowerCase:
                    if (!this.Selection.ReadOnly)
                    {
                        this.LowerCase();
                    }
                    break;

                case FCTBAction.UpperCase:
                    if (!this.Selection.ReadOnly)
                    {
                        this.UpperCase();
                    }
                    break;

                case FCTBAction.IndentDecrease:
                    if (!this.Selection.ReadOnly)
                    {
                        this.DecreaseIndent();
                    }
                    break;

                case FCTBAction.IndentIncrease:
                    if (!this.Selection.ReadOnly)
                    {
                        if (this.Selection.Start == this.Selection.End
                            && this[this.Selection.Start.iLine].StartSpacesCount < this.Selection.Start.iChar)
                        {
                            this.ProcessKey('\t', Keys.None);
                        }
                        else
                        {
                            this.IncreaseIndent();
                        }
                    }
                    break;

                case FCTBAction.AutoIndentChars:
                    if (!this.Selection.ReadOnly)
                    {
                        this.DoAutoIndentChars(this.Selection.Start.iLine);
                    }
                    break;

                case FCTBAction.NavigateBackward:
                    this.NavigateBackward();
                    break;

                case FCTBAction.NavigateForward:
                    this.NavigateForward();
                    break;

                case FCTBAction.UnbookmarkLine:
                    this.UnbookmarkLine(this.Selection.Start.iLine);
                    break;

                case FCTBAction.BookmarkLine:
                    this.BookmarkLine(this.Selection.Start.iLine);
                    break;

                case FCTBAction.GoNextBookmark:
                    this.GotoNextBookmark(this.Selection.Start.iLine);
                    break;

                case FCTBAction.GoPrevBookmark:
                    this.GotoPrevBookmark(this.Selection.Start.iLine);
                    break;

                case FCTBAction.ClearWordLeft:
                    if (this.OnKeyPressing('\b')) //KeyPress event processed key
                    {
                        break;
                    }
                    if (!this.Selection.ReadOnly)
                    {
                        if (!this.Selection.IsEmpty)
                        {
                            this.ClearSelected();
                        }
                        this.Selection.GoWordLeft(true);
                        if (!this.Selection.ReadOnly)
                        {
                            this.ClearSelected();
                        }
                    }
                    this.OnKeyPressed('\b');
                    break;

                case FCTBAction.ReplaceMode:
                    if (!this.ReadOnly)
                    {
                        this.isReplaceMode = !this.isReplaceMode;
                    }
                    break;

                case FCTBAction.DeleteCharRight:
                    if (!this.Selection.ReadOnly)
                    {
                        if (this.OnKeyPressing((char)0xff)) //KeyPress event processed key
                        {
                            break;
                        }
                        if (!this.Selection.IsEmpty)
                        {
                            this.ClearSelected();
                        }
                        else
                        {
                            //if line contains only spaces then delete line
                            if (this[this.Selection.Start.iLine].StartSpacesCount
                                == this[this.Selection.Start.iLine].Count)
                            {
                                this.RemoveSpacesAfterCaret();
                            }

                            if (!this.Selection.IsReadOnlyRightChar())
                            {
                                if (this.Selection.GoRightThroughFolded())
                                {
                                    int iLine = this.Selection.Start.iLine;

                                    this.InsertChar('\b');

                                    //if removed \n then trim spaces
                                    if (iLine != this.Selection.Start.iLine && this.AutoIndent)
                                    {
                                        if (this.Selection.Start.iChar > 0)
                                        {
                                            this.RemoveSpacesAfterCaret();
                                        }
                                    }
                                }
                            }
                        }

                        if (this.AutoIndentChars)
                        {
                            this.DoAutoIndentChars(this.Selection.Start.iLine);
                        }

                        this.OnKeyPressed((char)0xff);
                    }
                    break;

                case FCTBAction.ClearWordRight:
                    if (this.OnKeyPressing((char)0xff)) //KeyPress event processed key
                    {
                        break;
                    }
                    if (!this.Selection.ReadOnly)
                    {
                        if (!this.Selection.IsEmpty)
                        {
                            this.ClearSelected();
                        }
                        this.Selection.GoWordRight(true);
                        if (!this.Selection.ReadOnly)
                        {
                            this.ClearSelected();
                        }
                    }
                    this.OnKeyPressed((char)0xff);
                    break;

                case FCTBAction.GoWordLeft:
                    this.Selection.GoWordLeft(false);
                    break;

                case FCTBAction.GoWordLeftWithSelection:
                    this.Selection.GoWordLeft(true);
                    break;

                case FCTBAction.GoLeft:
                    this.Selection.GoLeft(false);
                    break;

                case FCTBAction.GoLeftWithSelection:
                    this.Selection.GoLeft(true);
                    break;

                case FCTBAction.GoLeft_ColumnSelectionMode:
                    this.CheckAndChangeSelectionType();
                    if (this.Selection.ColumnSelectionMode)
                    {
                        this.Selection.GoLeft_ColumnSelectionMode();
                    }
                    this.Invalidate();
                    break;

                case FCTBAction.GoWordRight:
                    this.Selection.GoWordRight(false);
                    break;

                case FCTBAction.GoWordRightWithSelection:
                    this.Selection.GoWordRight(true);
                    break;

                case FCTBAction.GoRight:
                    this.Selection.GoRight(false);
                    break;

                case FCTBAction.GoRightWithSelection:
                    this.Selection.GoRight(true);
                    break;

                case FCTBAction.GoRight_ColumnSelectionMode:
                    this.CheckAndChangeSelectionType();
                    if (this.Selection.ColumnSelectionMode)
                    {
                        this.Selection.GoRight_ColumnSelectionMode();
                    }
                    this.Invalidate();
                    break;

                case FCTBAction.GoUp:
                    this.Selection.GoUp(false);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoUpWithSelection:
                    this.Selection.GoUp(true);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoUp_ColumnSelectionMode:
                    this.CheckAndChangeSelectionType();
                    if (this.Selection.ColumnSelectionMode)
                    {
                        this.Selection.GoUp_ColumnSelectionMode();
                    }
                    this.Invalidate();
                    break;

                case FCTBAction.MoveSelectedLinesUp:
                    if (!this.Selection.ColumnSelectionMode)
                    {
                        this.MoveSelectedLinesUp();
                    }
                    break;

                case FCTBAction.GoDown:
                    this.Selection.GoDown(false);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoDownWithSelection:
                    this.Selection.GoDown(true);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoDown_ColumnSelectionMode:
                    this.CheckAndChangeSelectionType();
                    if (this.Selection.ColumnSelectionMode)
                    {
                        this.Selection.GoDown_ColumnSelectionMode();
                    }
                    this.Invalidate();
                    break;

                case FCTBAction.MoveSelectedLinesDown:
                    if (!this.Selection.ColumnSelectionMode)
                    {
                        this.MoveSelectedLinesDown();
                    }
                    break;
                case FCTBAction.GoPageUp:
                    this.Selection.GoPageUp(false);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoPageUpWithSelection:
                    this.Selection.GoPageUp(true);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoPageDown:
                    this.Selection.GoPageDown(false);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoPageDownWithSelection:
                    this.Selection.GoPageDown(true);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoFirstLine:
                    this.Selection.GoFirst(false);
                    break;

                case FCTBAction.GoFirstLineWithSelection:
                    this.Selection.GoFirst(true);
                    break;

                case FCTBAction.GoHome:
                    this.GoHome(false);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoHomeWithSelection:
                    this.GoHome(true);
                    this.ScrollLeft();
                    break;

                case FCTBAction.GoLastLine:
                    this.Selection.GoLast(false);
                    break;

                case FCTBAction.GoLastLineWithSelection:
                    this.Selection.GoLast(true);
                    break;

                case FCTBAction.GoEnd:
                    this.Selection.GoEnd(false);
                    break;

                case FCTBAction.GoEndWithSelection:
                    this.Selection.GoEnd(true);
                    break;

                case FCTBAction.ClearHints:
                    this.ClearHints();
                    if (this.MacrosManager != null)
                    {
                        this.MacrosManager.IsRecording = false;
                    }
                    break;

                case FCTBAction.MacroRecord:
                    if (this.MacrosManager != null)
                    {
                        if (this.MacrosManager.AllowMacroRecordingByUser)
                        {
                            this.MacrosManager.IsRecording = !this.MacrosManager.IsRecording;
                        }
                        if (this.MacrosManager.IsRecording)
                        {
                            this.MacrosManager.ClearMacros();
                        }
                    }
                    break;

                case FCTBAction.MacroExecute:
                    if (this.MacrosManager != null)
                    {
                        this.MacrosManager.IsRecording = false;
                        this.MacrosManager.ExecuteMacros();
                    }
                    break;
                case FCTBAction.CustomAction1:
                case FCTBAction.CustomAction2:
                case FCTBAction.CustomAction3:
                case FCTBAction.CustomAction4:
                case FCTBAction.CustomAction5:
                case FCTBAction.CustomAction6:
                case FCTBAction.CustomAction7:
                case FCTBAction.CustomAction8:
                case FCTBAction.CustomAction9:
                case FCTBAction.CustomAction10:
                case FCTBAction.CustomAction11:
                case FCTBAction.CustomAction12:
                case FCTBAction.CustomAction13:
                case FCTBAction.CustomAction14:
                case FCTBAction.CustomAction15:
                case FCTBAction.CustomAction16:
                case FCTBAction.CustomAction17:
                case FCTBAction.CustomAction18:
                case FCTBAction.CustomAction19:
                case FCTBAction.CustomAction20:
                    this.OnCustomAction(new CustomActionEventArgs(action));
                    break;
            }
        }

        protected virtual void OnCustomAction(CustomActionEventArgs e)
        {
            if (this.CustomAction != null)
            {
                this.CustomAction(this, e);
            }
        }

        private Font originalFont;

        private void RestoreFontSize()
        {
            this.Zoom = 100;
        }

        /// <summary>
        /// 	Scrolls to nearest bookmark or to first bookmark
        /// </summary>
        /// <param name="iLine"> Current bookmark line index </param>
        public bool GotoNextBookmark(int iLine)
        {
            Bookmark nearestBookmark = null;
            int minNextLineIndex = int.MaxValue;
            Bookmark minBookmark = null;
            int minLineIndex = int.MaxValue;
            foreach (var bookmark in this.bookmarks)
            {
                if (bookmark.LineIndex < minLineIndex)
                {
                    minLineIndex = bookmark.LineIndex;
                    minBookmark = bookmark;
                }

                if (bookmark.LineIndex > iLine && bookmark.LineIndex < minNextLineIndex)
                {
                    minNextLineIndex = bookmark.LineIndex;
                    nearestBookmark = bookmark;
                }
            }

            if (nearestBookmark != null)
            {
                nearestBookmark.DoVisible();
                return true;
            }
            else if (minBookmark != null)
            {
                minBookmark.DoVisible();
                return true;
            }

            return false;
        }

        /// <summary>
        /// 	Scrolls to nearest previous bookmark or to last bookmark
        /// </summary>
        /// <param name="iLine"> Current bookmark line index </param>
        public bool GotoPrevBookmark(int iLine)
        {
            Bookmark nearestBookmark = null;
            int maxPrevLineIndex = -1;
            Bookmark maxBookmark = null;
            int maxLineIndex = -1;
            foreach (var bookmark in this.bookmarks)
            {
                if (bookmark.LineIndex > maxLineIndex)
                {
                    maxLineIndex = bookmark.LineIndex;
                    maxBookmark = bookmark;
                }

                if (bookmark.LineIndex < iLine && bookmark.LineIndex > maxPrevLineIndex)
                {
                    maxPrevLineIndex = bookmark.LineIndex;
                    nearestBookmark = bookmark;
                }
            }

            if (nearestBookmark != null)
            {
                nearestBookmark.DoVisible();
                return true;
            }
            else if (maxBookmark != null)
            {
                maxBookmark.DoVisible();
                return true;
            }

            return false;
        }

        /// <summary>
        /// 	Bookmarks line
        /// </summary>
        public virtual void BookmarkLine(int iLine)
        {
            if (!this.bookmarks.Contains(iLine))
            {
                this.bookmarks.Add(iLine);
            }
        }

        /// <summary>
        /// 	Unbookmarks current line
        /// </summary>
        public virtual void UnbookmarkLine(int iLine)
        {
            this.bookmarks.Remove(iLine);
        }

        /// <summary>
        /// 	Moves selected lines down
        /// </summary>
        public virtual void MoveSelectedLinesDown()
        {
            Range prevSelection = this.Selection.Clone();
            this.Selection.Expand();
            if (!this.Selection.ReadOnly)
            {
                int iLine = this.Selection.Start.iLine;
                if (this.Selection.End.iLine >= this.LinesCount - 1)
                {
                    this.Selection = prevSelection;
                    return;
                }
                string text = this.SelectedText;
                var temp = new List<int>();
                for (int i = this.Selection.Start.iLine; i <= this.Selection.End.iLine; i++)
                {
                    temp.Add(i);
                }
                this.RemoveLines(temp);
                this.Selection.Start = new Place(this.GetLineLength(iLine), iLine);
                this.SelectedText = "\n" + text;
                this.Selection.Start = new Place(prevSelection.Start.iChar, prevSelection.Start.iLine + 1);
                this.Selection.End = new Place(prevSelection.End.iChar, prevSelection.End.iLine + 1);
            }
            else
            {
                this.Selection = prevSelection;
            }
        }

        /// <summary>
        /// 	Moves selected lines up
        /// </summary>
        public virtual void MoveSelectedLinesUp()
        {
            Range prevSelection = this.Selection.Clone();
            this.Selection.Expand();
            if (!this.Selection.ReadOnly)
            {
                int iLine = this.Selection.Start.iLine;
                if (iLine == 0)
                {
                    this.Selection = prevSelection;
                    return;
                }
                string text = this.SelectedText;
                var temp = new List<int>();
                for (int i = this.Selection.Start.iLine; i <= this.Selection.End.iLine; i++)
                {
                    temp.Add(i);
                }
                this.RemoveLines(temp);
                this.Selection.Start = new Place(0, iLine - 1);
                this.SelectedText = text + "\n";
                this.Selection.Start = new Place(prevSelection.Start.iChar, prevSelection.Start.iLine - 1);
                this.Selection.End = new Place(prevSelection.End.iChar, prevSelection.End.iLine - 1);
            }
            else
            {
                this.Selection = prevSelection;
            }
        }

        private void GoHome(bool shift)
        {
            this.Selection.BeginUpdate();
            try
            {
                int iLine = this.Selection.Start.iLine;
                int spaces = this[iLine].StartSpacesCount;
                if (this.Selection.Start.iChar <= spaces)
                {
                    this.Selection.GoHome(shift);
                }
                else
                {
                    this.Selection.GoHome(shift);
                    for (int i = 0; i < spaces; i++)
                    {
                        this.Selection.GoRight(shift);
                    }
                }
            }
            finally
            {
                this.Selection.EndUpdate();
            }
        }

        /// <summary>
        /// 	Convert selected text to upper case
        /// </summary>
        public void UpperCase()
        {
            Range old = this.Selection.Clone();
            this.SelectedText = this.SelectedText.ToUpper();
            this.Selection.Start = old.Start;
            this.Selection.End = old.End;
        }

        /// <summary>
        /// 	Convert selected text to lower case
        /// </summary>
        public void LowerCase()
        {
            Range old = this.Selection.Clone();
            this.SelectedText = this.SelectedText.ToLower();
            this.Selection.Start = old.Start;
            this.Selection.End = old.End;
        }

        /// <summary>
        /// 	Insert/remove comment prefix into selected lines
        /// </summary>
        public void CommentSelected()
        {
            this.CommentSelected(this.CommentPrefix);
        }

        /// <summary>
        /// 	Insert/remove comment prefix into selected lines
        /// </summary>
        public virtual void CommentSelected(string commentPrefix)
        {
            if (string.IsNullOrEmpty(commentPrefix))
            {
                return;
            }
            this.Selection.Normalize();
            bool isCommented = this.lines[this.Selection.Start.iLine].Text.TrimStart().StartsWith(commentPrefix);
            if (isCommented)
            {
                this.RemoveLinePrefix(commentPrefix);
            }
            else
            {
                this.InsertLinePrefix(commentPrefix);
            }
        }

        public void OnKeyPressing(KeyPressEventArgs args)
        {
            if (this.KeyPressing != null)
            {
                this.KeyPressing(this, args);
            }
        }

        private bool OnKeyPressing(char c)
        {
            if (this.findCharMode)
            {
                this.findCharMode = false;
                this.FindChar(c);
                return true;
            }
            var args = new KeyPressEventArgs(c);
            OnKeyPressing(args);
            return args.Handled;
        }

        public void OnKeyPressed(char c)
        {
            var args = new KeyPressEventArgs(c);
            if (this.KeyPressed != null)
            {
                this.KeyPressed(this, args);
            }
        }

        protected override bool ProcessMnemonic(char charCode)
        {
            if (this.middleClickScrollingActivated)
            {
                return false;
            }

            if (this.Focused)
            {
                return this.ProcessKey(charCode, this.lastModifiers) || base.ProcessMnemonic(charCode);
            }
            else
            {
                return false;
            }
        }

        private const int WM_CHAR = 0x102;

        protected override bool ProcessKeyMessage(ref Message m)
        {
            if (m.Msg == WM_CHAR)
            {
                this.ProcessMnemonic(Convert.ToChar(m.WParam.ToInt32()));
            }

            return base.ProcessKeyMessage(ref m);
        }

        /// <summary>
        /// 	Process "real" keys (no control)
        /// </summary>
        public virtual bool ProcessKey(char c, Keys modifiers)
        {
            if (this.handledChar)
            {
                return true;
            }

            if (this.macrosManager != null)
            {
                this.macrosManager.ProcessKey(c, modifiers);
            }
            /*  !!!!
            if (c == ' ')
                return true;*/

            //backspace
            if (c == '\b' && (modifiers == Keys.None || modifiers == Keys.Shift || (modifiers & Keys.Alt) != 0))
            {
                if (this.ReadOnly || !this.Enabled)
                {
                    return false;
                }

                if (OnKeyPressing(c))
                {
                    return true;
                }

                if (this.Selection.ReadOnly)
                {
                    return false;
                }

                if (!this.Selection.IsEmpty)
                {
                    this.ClearSelected();
                }
                else if (!this.Selection.IsReadOnlyLeftChar()) //is not left char readonly?
                {
                    this.InsertChar('\b');
                }

                if (this.AutoIndentChars)
                {
                    this.DoAutoIndentChars(this.Selection.Start.iLine);
                }

                this.OnKeyPressed('\b');
                return true;
            }

            /* !!!!
            if (c == '\b' && (modifiers & Keys.Alt) != 0)
                return true;*/

            if (char.IsControl(c) && c != '\r' && c != '\t')
            {
                return false;
            }

            if (this.ReadOnly || !this.Enabled)
            {
                return false;
            }

            if (modifiers != Keys.None && modifiers != Keys.Shift && modifiers != (Keys.Control | Keys.Alt)
                && //ALT+CTRL is special chars (AltGr)
                modifiers != (Keys.Shift | Keys.Control | Keys.Alt) && //SHIFT + ALT + CTRL is special chars (AltGr)
                (modifiers != (Keys.Alt) || char.IsLetterOrDigit(c)) //may be ALT+LetterOrDigit is mnemonic code
                )
            {
                return false; //do not process Ctrl+? and Alt+? keys
            }

            char sourceC = c;
            if (OnKeyPressing(sourceC)) //KeyPress event processed key
            {
                return true;
            }

            //
            if (this.Selection.ReadOnly)
            {
                return false;
            }
            //
            if (c == '\r' && !this.AcceptsReturn)
            {
                return false;
            }

            //replace \r on \n
            if (c == '\r')
            {
                c = '\n';
            }
            //replace mode? select forward char
            if (this.IsReplaceMode)
            {
                this.Selection.GoRight(true);
                this.Selection.Inverse();
            }
            //insert char
            if (!this.Selection.ReadOnly)
            {
                if (!this.DoAutocompleteBrackets(c))
                {
                    this.InsertChar(c);
                }
            }

            //do autoindent
            if (c == '\n' || this.AutoIndentExistingLines)
            {
                this.DoAutoIndentIfNeed();
            }

            if (this.AutoIndentChars)
            {
                this.DoAutoIndentChars(this.Selection.Start.iLine);
            }

            this.DoCaretVisible();
            this.Invalidate();

            this.OnKeyPressed(sourceC);

            return true;
        }

        #region AutoIndentChars

        /// <summary>
        /// 	Enables AutoIndentChars mode
        /// </summary>
        [Description("Enables AutoIndentChars mode")]
        [DefaultValue(true)]
        public bool AutoIndentChars { get; set; }

        /// <summary>
        /// 	Regex patterns for AutoIndentChars (one regex per line)
        /// </summary>
        [Description("Regex patterns for AutoIndentChars (one regex per line)")]
        [Editor(
            "System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            , typeof(UITypeEditor))]
        [DefaultValue(@"^\s*[\w\.]+\s*(?<range>=)\s*(?<range>[^;]+);")]
        public string AutoIndentCharsPatterns { get; set; }

        /// <summary>
        /// 	Do AutoIndentChars
        /// </summary>
        public void DoAutoIndentChars(int iLine)
        {
            string[] patterns = this.AutoIndentCharsPatterns.Split(
                new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var pattern in patterns)
            {
                Match m = Regex.Match(this[iLine].Text, pattern);
                if (m.Success)
                {
                    this.DoAutoIndentChars(iLine, new Regex(pattern));
                    break;
                }
            }
        }

        protected void DoAutoIndentChars(int iLine, Regex regex)
        {
            Range oldSel = this.Selection.Clone();

            var captures = new SortedDictionary<int, CaptureCollection>();
            var texts = new SortedDictionary<int, string>();
            int maxCapturesCount = 0;

            int spaces = this[iLine].StartSpacesCount;

            for (int i = iLine; i >= 0; i--)
            {
                if (spaces != this[i].StartSpacesCount)
                {
                    break;
                }

                string text = this[i].Text;
                Match m = regex.Match(text);
                if (m.Success)
                {
                    captures[i] = m.Groups["range"].Captures;
                    texts[i] = text;

                    if (captures[i].Count > maxCapturesCount)
                    {
                        maxCapturesCount = captures[i].Count;
                    }
                }
                else
                {
                    break;
                }
            }

            for (int i = iLine + 1; i < this.LinesCount; i++)
            {
                if (spaces != this[i].StartSpacesCount)
                {
                    break;
                }

                string text = this[i].Text;
                Match m = regex.Match(text);
                if (m.Success)
                {
                    captures[i] = m.Groups["range"].Captures;
                    texts[i] = text;

                    if (captures[i].Count > maxCapturesCount)
                    {
                        maxCapturesCount = captures[i].Count;
                    }
                }
                else
                {
                    break;
                }
            }

            var changed = new Dictionary<int, bool>();
            bool was = false;

            for (int iCapture = maxCapturesCount - 1; iCapture >= 0; iCapture--)
            {
                //find max dist
                int maxDist = 0;
                foreach (var i in captures.Keys)
                {
                    CaptureCollection caps = captures[i];
                    if (caps.Count <= iCapture)
                    {
                        continue;
                    }
                    int dist = 0;
                    Capture cap = caps[iCapture];

                    int index = cap.Index;

                    string text = texts[i];
                    while (index > 0 && text[index - 1] == ' ')
                    {
                        index--;
                    }

                    if (iCapture == 0)
                    {
                        dist = index;
                    }
                    else
                    {
                        dist = index - caps[iCapture - 1].Index - 1;
                    }

                    if (dist > maxDist)
                    {
                        maxDist = dist;
                    }
                }

                //insert whitespaces
                foreach (var i in new List<int>(texts.Keys))
                {
                    if (captures[i].Count <= iCapture)
                    {
                        continue;
                    }

                    int dist = 0;
                    Capture cap = captures[i][iCapture];

                    if (iCapture == 0)
                    {
                        dist = cap.Index;
                    }
                    else
                    {
                        dist = cap.Index - captures[i][iCapture - 1].Index - 1;
                    }

                    int addSpaces = maxDist - dist + 1; //+1 because min space count is 1

                    if (addSpaces == 0)
                    {
                        continue;
                    }

                    if (oldSel.Start.iLine == i && oldSel.Start.iChar > cap.Index)
                    {
                        oldSel.Start = new Place(oldSel.Start.iChar + addSpaces, i);
                    }

                    if (addSpaces > 0)
                    {
                        texts[i] = texts[i].Insert(cap.Index, new string(' ', addSpaces));
                    }
                    else
                    {
                        texts[i] = texts[i].Remove(cap.Index + addSpaces, -addSpaces);
                    }

                    changed[i] = true;
                    was = true;
                }
            }

            //insert text
            if (was)
            {
                this.Selection.BeginUpdate();
                this.BeginAutoUndo();
                this.BeginUpdate();

                this.TextSource.Manager.ExecuteCommand(new SelectCommand(this.TextSource));

                foreach (var i in texts.Keys)
                {
                    if (changed.ContainsKey(i))
                    {
                        this.Selection = new Range(this, 0, i, this[i].Count, i);
                        if (!this.Selection.ReadOnly)
                        {
                            this.InsertText(texts[i]);
                        }
                    }
                }

                this.Selection = oldSel;

                this.EndUpdate();
                this.EndAutoUndo();
                this.Selection.EndUpdate();
            }
        }

        #endregion

        private bool DoAutocompleteBrackets(char c)
        {
            if (this.AutoCompleteBrackets)
            {
                if (!this.Selection.ColumnSelectionMode)
                {
                    for (int i = 1; i < this.autoCompleteBracketsList.Length; i += 2)
                    {
                        if (c == this.autoCompleteBracketsList[i] && c == this.Selection.CharAfterStart)
                        {
                            this.Selection.GoRight();
                            return true;
                        }
                    }
                }

                for (int i = 0; i < this.autoCompleteBracketsList.Length; i += 2)
                {
                    if (c == this.autoCompleteBracketsList[i])
                    {
                        this.InsertBrackets(this.autoCompleteBracketsList[i], this.autoCompleteBracketsList[i + 1]);
                        return true;
                    }
                }
            }
            return false;
        }

        private bool InsertBrackets(char left, char right)
        {
            if (this.Selection.ColumnSelectionMode)
            {
                Range range = this.Selection.Clone();
                range.Normalize();
                this.Selection.BeginUpdate();
                this.BeginAutoUndo();
                this.Selection = new Range(
                    this, range.Start.iChar, range.Start.iLine, range.Start.iChar, range.End.iLine)
                    { ColumnSelectionMode = true };
                this.InsertChar(left);
                this.Selection = new Range(
                    this, range.End.iChar + 1, range.Start.iLine, range.End.iChar + 1, range.End.iLine)
                    { ColumnSelectionMode = true };
                this.InsertChar(right);
                if (range.IsEmpty)
                {
                    this.Selection = new Range(
                        this, range.End.iChar + 1, range.Start.iLine, range.End.iChar + 1, range.End.iLine)
                        { ColumnSelectionMode = true };
                }
                this.EndAutoUndo();
                this.Selection.EndUpdate();
            }
            else if (this.Selection.IsEmpty)
            {
                this.InsertText(left + "" + right);
                this.Selection.GoLeft();
            }
            else
            {
                this.InsertText(left + this.SelectedText + right);
            }

            return true;
        }

        /// <summary>
        /// 	Finds given char after current caret position, moves the caret to found pos.
        /// </summary>
        /// <param name="c"> </param>
        protected virtual void FindChar(char c)
        {
            if (c == '\r')
            {
                c = '\n';
            }

            Range r = this.Selection.Clone();
            while (r.GoRight())
            {
                if (r.CharBeforeStart == c)
                {
                    this.Selection = r;
                    this.DoCaretVisible();
                    return;
                }
            }
        }

        public virtual void DoAutoIndentIfNeed()
        {
            if (this.Selection.ColumnSelectionMode)
            {
                return;
            }
            if (this.AutoIndent)
            {
                this.DoCaretVisible();
                int needSpaces = this.CalcAutoIndent(this.Selection.Start.iLine);
                if (this[this.Selection.Start.iLine].AutoIndentSpacesNeededCount != needSpaces)
                {
                    this.DoAutoIndent(this.Selection.Start.iLine);
                    this[this.Selection.Start.iLine].AutoIndentSpacesNeededCount = needSpaces;
                }
            }
        }

        private void RemoveSpacesAfterCaret()
        {
            if (!this.Selection.IsEmpty)
            {
                return;
            }
            Place end = this.Selection.Start;
            while (this.Selection.CharAfterStart == ' ')
            {
                this.Selection.GoRight(true);
            }
            this.ClearSelected();
        }

        /// <summary>
        /// 	Inserts autoindent's spaces in the line
        /// </summary>
        public virtual void DoAutoIndent(int iLine)
        {
            if (this.Selection.ColumnSelectionMode)
            {
                return;
            }
            Place oldStart = this.Selection.Start;
            //
            int needSpaces = this.CalcAutoIndent(iLine);
            //
            int spaces = this.lines[iLine].StartSpacesCount;
            int needToInsert = needSpaces - spaces;
            if (needToInsert < 0)
            {
                needToInsert = -Math.Min(-needToInsert, spaces);
            }
            //insert start spaces
            if (needToInsert == 0)
            {
                return;
            }
            this.Selection.Start = new Place(0, iLine);
            if (needToInsert > 0)
            {
                this.InsertText(new String(' ', needToInsert));
            }
            else
            {
                this.Selection.Start = new Place(0, iLine);
                this.Selection.End = new Place(-needToInsert, iLine);
                this.ClearSelected();
            }

            this.Selection.Start =
                new Place(Math.Min(this.lines[iLine].Count, Math.Max(0, oldStart.iChar + needToInsert)), iLine);
        }

        /// <summary>
        /// 	Returns needed start space count for the line
        /// </summary>
        public virtual int CalcAutoIndent(int iLine)
        {
            if (iLine < 0 || iLine >= this.LinesCount)
            {
                return 0;
            }

            EventHandler<AutoIndentEventArgs> calculator = this.AutoIndentNeeded;
            if (calculator == null)
            {
                if (this.Language != Language.Custom && this.SyntaxHighlighter != null)
                {
                    calculator = this.SyntaxHighlighter.AutoIndentNeeded;
                }
                else
                {
                    calculator = this.CalcAutoIndentShiftByCodeFolding;
                }
            }

            int needSpaces = 0;

            var stack = new Stack<AutoIndentEventArgs>();
            //calc indent for previous lines, find stable line
            int i;
            for (i = iLine - 1; i >= 0; i--)
            {
                var args = new AutoIndentEventArgs(
                    i, this.lines[i].Text, i > 0 ? this.lines[i - 1].Text : "", this.TabLength, 0);
                calculator(this, args);
                stack.Push(args);
                if (args.Shift == 0 && args.AbsoluteIndentation == 0 && args.LineText.Trim() != "")
                {
                    break;
                }
            }
            int indent = this.lines[i >= 0 ? i : 0].StartSpacesCount;
            while (stack.Count != 0)
            {
                AutoIndentEventArgs arg = stack.Pop();
                if (arg.AbsoluteIndentation != 0)
                {
                    indent = arg.AbsoluteIndentation + arg.ShiftNextLines;
                }
                else
                {
                    indent += arg.ShiftNextLines;
                }
            }
            //clalc shift for current line
            var a = new AutoIndentEventArgs(
                iLine, this.lines[iLine].Text, iLine > 0 ? this.lines[iLine - 1].Text : "", this.TabLength, indent);
            calculator(this, a);
            needSpaces = a.AbsoluteIndentation + a.Shift;

            return needSpaces;
        }

        internal virtual void CalcAutoIndentShiftByCodeFolding(object sender, AutoIndentEventArgs args)
        {
            //inset TAB after start folding marker
            if (string.IsNullOrEmpty(this.lines[args.iLine].FoldingEndMarker)
                && !string.IsNullOrEmpty(this.lines[args.iLine].FoldingStartMarker))
            {
                args.ShiftNextLines = this.TabLength;
                return;
            }
            //remove TAB before end folding marker
            if (!string.IsNullOrEmpty(this.lines[args.iLine].FoldingEndMarker)
                && string.IsNullOrEmpty(this.lines[args.iLine].FoldingStartMarker))
            {
                args.Shift = -this.TabLength;
                args.ShiftNextLines = -this.TabLength;
                return;
            }
        }

        protected int GetMinStartSpacesCount(int fromLine, int toLine)
        {
            if (fromLine > toLine)
            {
                return 0;
            }

            int result = int.MaxValue;
            for (int i = fromLine; i <= toLine; i++)
            {
                int count = this.lines[i].StartSpacesCount;
                if (count < result)
                {
                    result = count;
                }
            }

            return result;
        }

        protected int GetMaxStartSpacesCount(int fromLine, int toLine)
        {
            if (fromLine > toLine)
            {
                return 0;
            }

            int result = 0;
            for (int i = fromLine; i <= toLine; i++)
            {
                int count = this.lines[i].StartSpacesCount;
                if (count > result)
                {
                    result = count;
                }
            }

            return result;
        }

        /// <summary>
        /// 	Undo last operation
        /// </summary>
        public virtual void Undo()
        {
            this.lines.Manager.Undo();
            this.DoCaretVisible();
            this.Invalidate();
        }

        /// <summary>
        /// 	Redo
        /// </summary>
        public virtual void Redo()
        {
            this.lines.Manager.Redo();
            this.DoCaretVisible();
            this.Invalidate();
        }

        protected override bool IsInputKey(Keys keyData)
        {
            if (keyData == Keys.Tab && !this.AcceptsTab)
            {
                return false;
            }
            if (keyData == Keys.Enter && !this.AcceptsReturn)
            {
                return false;
            }

            if ((keyData & Keys.Alt) == Keys.None)
            {
                Keys keys = keyData & Keys.KeyCode;
                if (keys == Keys.Return)
                {
                    return true;
                }
            }

            if ((keyData & Keys.Alt) != Keys.Alt)
            {
                switch ((keyData & Keys.KeyCode))
                {
                    case Keys.Prior:
                    case Keys.Next:
                    case Keys.End:
                    case Keys.Home:
                    case Keys.Left:
                    case Keys.Right:
                    case Keys.Up:
                    case Keys.Down:
                        return true;

                    case Keys.Escape:
                        return false;

                    case Keys.Tab:
                        return (keyData & Keys.Control) == Keys.None;
                }
            }

            return base.IsInputKey(keyData);
        }

        [DllImport("User32.dll")]
        private static extern bool CreateCaret(IntPtr hWnd, int hBitmap, int nWidth, int nHeight);

        [DllImport("User32.dll")]
        private static extern bool SetCaretPos(int x, int y);

        [DllImport("User32.dll")]
        private static extern bool DestroyCaret();

        [DllImport("User32.dll")]
        private static extern bool ShowCaret(IntPtr hWnd);

        [DllImport("User32.dll")]
        private static extern bool HideCaret(IntPtr hWnd);

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            if (this.BackBrush == null)
            {
                base.OnPaintBackground(e);
            }
            else
            {
                e.Graphics.FillRectangle(this.BackBrush, this.ClientRectangle);
            }
        }

        /// <summary>
        /// 	Draws text to given Graphics
        /// </summary>
        /// <param name="gr"> </param>
        /// <param name="start"> Start place of drawing text </param>
        /// <param name="size"> Size of drawing </param>
        public void DrawText(Graphics gr, Place start, Size size)
        {
            if (this.needRecalc)
            {
                this.Recalc();
            }

            if (this.needRecalcFoldingLines)
            {
                this.RecalcFoldingLines();
            }

            Point startPoint = this.PlaceToPoint(start);
            int startY = startPoint.Y + this.VerticalScroll.Value;
            int startX = startPoint.X + this.HorizontalScroll.Value - this.LeftIndent - this.Paddings.Left;
            int firstChar = start.iChar;
            int lastChar = (startX + size.Width) / this.CharWidth;

            int startLine = start.iLine;
            //draw text
            for (int iLine = startLine; iLine < this.lines.Count; iLine++)
            {
                Line line = this.lines[iLine];
                LineInfo lineInfo = this.LineInfos[iLine];
                //
                if (lineInfo.startY > startY + size.Height)
                {
                    break;
                }
                if (lineInfo.startY + lineInfo.WordWrapStringsCount * this.CharHeight < startY)
                {
                    continue;
                }
                if (lineInfo.VisibleState == VisibleState.Hidden)
                {
                    continue;
                }

                int y = lineInfo.startY - startY;
                //
                gr.SmoothingMode = SmoothingMode.None;
                //draw line background
                if (lineInfo.VisibleState == VisibleState.Visible)
                {
                    if (line.BackgroundBrush != null)
                    {
                        gr.FillRectangle(
                            line.BackgroundBrush,
                            new Rectangle(0, y, size.Width, this.CharHeight * lineInfo.WordWrapStringsCount));
                    }
                }
                //
                gr.SmoothingMode = SmoothingMode.AntiAlias;

                //draw wordwrap strings of line
                for (int iWordWrapLine = 0; iWordWrapLine < lineInfo.WordWrapStringsCount; iWordWrapLine++)
                {
                    y = lineInfo.startY + iWordWrapLine * this.CharHeight - startY;
                    //indent 
                    int indent = iWordWrapLine == 0 ? 0 : lineInfo.wordWrapIndent * this.CharWidth;
                    //draw chars
                    this.DrawLineChars(gr, firstChar, lastChar, iLine, iWordWrapLine, -startX + indent, y);
                }
            }
        }

        /// <summary>
        /// 	Draw control
        /// </summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (this.needRecalc)
            {
                this.Recalc();
            }

            if (this.needRecalcFoldingLines)
            {
                this.RecalcFoldingLines();
            }
#if debug
            var sw = Stopwatch.StartNew();
#endif
            this.visibleMarkers.Clear();
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            //
            var servicePen = new Pen(this.ServiceLinesColor);
            Brush changedLineBrush = new SolidBrush(this.ChangedLineColor);
            Brush indentBrush = new SolidBrush(this.IndentBackColor);
            Brush paddingBrush = new SolidBrush(this.PaddingBackColor);
            Brush currentLineBrush =
                new SolidBrush(
                    Color.FromArgb(this.CurrentLineColor.A == 255 ? 50 : this.CurrentLineColor.A, this.CurrentLineColor));
            //draw padding area
            Rectangle textAreaRect = this.TextAreaRect;
            //top
            e.Graphics.FillRectangle(
                paddingBrush, 0, -this.VerticalScroll.Value, this.ClientSize.Width, Math.Max(0, this.Paddings.Top - 1));
            //bottom
            e.Graphics.FillRectangle(
                paddingBrush, 0, textAreaRect.Bottom, this.ClientSize.Width, this.ClientSize.Height);
            //right
            e.Graphics.FillRectangle(paddingBrush, textAreaRect.Right, 0, this.ClientSize.Width, this.ClientSize.Height);
            //left
            e.Graphics.FillRectangle(
                paddingBrush, this.LeftIndentLine, 0, this.LeftIndent - this.LeftIndentLine - 1, this.ClientSize.Height);
            if (this.HorizontalScroll.Value <= this.Paddings.Left)
            {
                e.Graphics.FillRectangle(
                    paddingBrush,
                    this.LeftIndent - this.HorizontalScroll.Value - 2,
                    0,
                    Math.Max(0, this.Paddings.Left - 1),
                    this.ClientSize.Height);
            }
            //
            int leftTextIndent = Math.Max(
                this.LeftIndent, this.LeftIndent + this.Paddings.Left - this.HorizontalScroll.Value);
            int textWidth = textAreaRect.Width;
            //draw indent area
            e.Graphics.FillRectangle(indentBrush, 0, 0, this.LeftIndentLine, this.ClientSize.Height);
            if (this.LeftIndent > minLeftIndent)
            {
                e.Graphics.DrawLine(servicePen, this.LeftIndentLine, 0, this.LeftIndentLine, this.ClientSize.Height);
            }
            //draw preferred line width
            if (this.PreferredLineWidth > 0)
            {
                e.Graphics.DrawLine(
                    servicePen,
                    new Point(
                        this.LeftIndent + this.Paddings.Left + this.PreferredLineWidth * this.CharWidth
                        - this.HorizontalScroll.Value + 1,
                        textAreaRect.Top + 1),
                    new Point(
                        this.LeftIndent + this.Paddings.Left + this.PreferredLineWidth * this.CharWidth
                        - this.HorizontalScroll.Value + 1,
                        textAreaRect.Bottom - 1));
            }

            //draw text area border
            this.DrawTextAreaBorder(e.Graphics);
            //
            int firstChar = (Math.Max(0, this.HorizontalScroll.Value - this.Paddings.Left)) / this.CharWidth;
            int lastChar = (this.HorizontalScroll.Value + this.ClientSize.Width) / this.CharWidth;
            //
            int x = this.LeftIndent + this.Paddings.Left - this.HorizontalScroll.Value;
            if (x < this.LeftIndent)
            {
                firstChar++;
            }
            //create dictionary of bookmarks
            var bookmarksByLineIndex = new Dictionary<int, Bookmark>();
            foreach (var item in this.bookmarks)
            {
                bookmarksByLineIndex[item.LineIndex] = item;
            }
            //
            int startLine = this.YtoLineIndex(this.VerticalScroll.Value);
            int iLine;
            //draw text
            for (iLine = startLine; iLine < this.lines.Count; iLine++)
            {
                Line line = this.lines[iLine];
                LineInfo lineInfo = this.LineInfos[iLine];
                //
                if (lineInfo.startY > this.VerticalScroll.Value + this.ClientSize.Height)
                {
                    break;
                }
                if (lineInfo.startY + lineInfo.WordWrapStringsCount * this.CharHeight < this.VerticalScroll.Value)
                {
                    continue;
                }
                if (lineInfo.VisibleState == VisibleState.Hidden)
                {
                    continue;
                }

                int y = lineInfo.startY - this.VerticalScroll.Value;
                //
                e.Graphics.SmoothingMode = SmoothingMode.None;
                //draw line background
                if (lineInfo.VisibleState == VisibleState.Visible)
                {
                    if (line.BackgroundBrush != null)
                    {
                        e.Graphics.FillRectangle(
                            line.BackgroundBrush,
                            new Rectangle(
                                textAreaRect.Left,
                                y,
                                textAreaRect.Width,
                                this.CharHeight * lineInfo.WordWrapStringsCount));
                    }
                }
                //draw current line background
                if (this.CurrentLineColor != Color.Transparent && iLine == this.Selection.Start.iLine)
                {
                    if (this.Selection.IsEmpty)
                    {
                        e.Graphics.FillRectangle(
                            currentLineBrush, new Rectangle(textAreaRect.Left, y, textAreaRect.Width, this.CharHeight));
                    }
                }
                //draw changed line marker
                if (this.ChangedLineColor != Color.Transparent && line.IsChanged)
                {
                    e.Graphics.FillRectangle(
                        changedLineBrush,
                        new RectangleF(-10, y, this.LeftIndent - minLeftIndent - 2 + 10, this.CharHeight + 1));
                }
                //
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                //
                //draw bookmark
                if (bookmarksByLineIndex.ContainsKey(iLine))
                {
                    bookmarksByLineIndex[iLine].Paint(
                        e.Graphics,
                        new Rectangle(this.LeftIndent, y, this.Width, this.CharHeight * lineInfo.WordWrapStringsCount));
                }
                //OnPaintLine event
                if (lineInfo.VisibleState == VisibleState.Visible)
                {
                    this.OnPaintLine(
                        new PaintLineEventArgs(
                            iLine,
                            new Rectangle(
                                this.LeftIndent, y, this.Width, this.CharHeight * lineInfo.WordWrapStringsCount),
                            e.Graphics,
                            e.ClipRectangle));
                }
                //draw line number
                if (this.ShowLineNumbers)
                {
                    using (var lineNumberBrush = new SolidBrush(this.LineNumberColor))
                        e.Graphics.DrawString(
                            (iLine + this.lineNumberStartValue).ToString(),
                            this.Font,
                            lineNumberBrush,
                            new RectangleF(-10, y, this.LeftIndent - minLeftIndent - 2 + 10, this.CharHeight),
                            new StringFormat(StringFormatFlags.DirectionRightToLeft));
                }
                //create markers
                if (lineInfo.VisibleState == VisibleState.StartOfHiddenBlock)
                {
                    this.visibleMarkers.Add(
                        new ExpandFoldingMarker(
                            iLine, new Rectangle(this.LeftIndentLine - 4, y + this.CharHeight / 2 - 3, 8, 8)));
                }
                if (!string.IsNullOrEmpty(line.FoldingStartMarker) && lineInfo.VisibleState == VisibleState.Visible
                    && string.IsNullOrEmpty(line.FoldingEndMarker))
                {
                    this.visibleMarkers.Add(
                        new CollapseFoldingMarker(
                            iLine, new Rectangle(this.LeftIndentLine - 4, y + this.CharHeight / 2 - 3, 8, 8)));
                }
                if (lineInfo.VisibleState == VisibleState.Visible && !string.IsNullOrEmpty(line.FoldingEndMarker)
                    && string.IsNullOrEmpty(line.FoldingStartMarker))
                {
                    e.Graphics.DrawLine(
                        servicePen,
                        this.LeftIndentLine,
                        y + this.CharHeight * lineInfo.WordWrapStringsCount - 1,
                        this.LeftIndentLine + 4,
                        y + this.CharHeight * lineInfo.WordWrapStringsCount - 1);
                }
                //draw wordwrap strings of line
                for (int iWordWrapLine = 0; iWordWrapLine < lineInfo.WordWrapStringsCount; iWordWrapLine++)
                {
                    y = lineInfo.startY + iWordWrapLine * this.CharHeight - this.VerticalScroll.Value;
                    //indent
                    int indent = iWordWrapLine == 0 ? 0 : lineInfo.wordWrapIndent * this.CharWidth;
                    //draw chars
                    this.DrawLineChars(e.Graphics, firstChar, lastChar, iLine, iWordWrapLine, x + indent, y);
                }
            }

            int endLine = iLine - 1;

            //draw folding lines
            if (this.ShowFoldingLines)
            {
                this.DrawFoldingLines(e, startLine, endLine);
            }

            //draw column selection
            if (this.Selection.ColumnSelectionMode)
            {
                if (this.SelectionStyle.BackgroundBrush is SolidBrush)
                {
                    Color color = ((SolidBrush)this.SelectionStyle.BackgroundBrush).Color;
                    Point p1 = this.PlaceToPoint(this.Selection.Start);
                    Point p2 = this.PlaceToPoint(this.Selection.End);
                    using (var pen = new Pen(color))
                        e.Graphics.DrawRectangle(
                            pen,
                            Rectangle.FromLTRB(
                                Math.Min(p1.X, p2.X) - 1,
                                Math.Min(p1.Y, p2.Y),
                                Math.Max(p1.X, p2.X),
                                Math.Max(p1.Y, p2.Y) + this.CharHeight));
                }
            }
            //draw brackets highlighting
            if (this.BracketsStyle != null && this.leftBracketPosition != null && this.rightBracketPosition != null)
            {
                this.BracketsStyle.Draw(
                    e.Graphics, this.PlaceToPoint(this.leftBracketPosition.Start), this.leftBracketPosition);
                this.BracketsStyle.Draw(
                    e.Graphics, this.PlaceToPoint(this.rightBracketPosition.Start), this.rightBracketPosition);
            }
            if (this.BracketsStyle2 != null && this.leftBracketPosition2 != null && this.rightBracketPosition2 != null)
            {
                this.BracketsStyle2.Draw(
                    e.Graphics, this.PlaceToPoint(this.leftBracketPosition2.Start), this.leftBracketPosition2);
                this.BracketsStyle2.Draw(
                    e.Graphics, this.PlaceToPoint(this.rightBracketPosition2.Start), this.rightBracketPosition2);
            }
            //
            e.Graphics.SmoothingMode = SmoothingMode.None;
            //draw folding indicator
            if ((this.startFoldingLine >= 0 || this.endFoldingLine >= 0) && this.Selection.Start == this.Selection.End)
            {
                if (this.endFoldingLine < this.LineInfos.Count)
                {
                    //folding indicator
                    int startFoldingY = (this.startFoldingLine >= 0 ? this.LineInfos[this.startFoldingLine].startY : 0)
                                        - this.VerticalScroll.Value + this.CharHeight / 2;
                    int endFoldingY = (this.endFoldingLine >= 0
                                           ? this.LineInfos[this.endFoldingLine].startY
                                             +
                                             (this.LineInfos[this.endFoldingLine].WordWrapStringsCount - 1)
                                             * this.CharHeight
                                           : this.TextHeight + this.CharHeight) - this.VerticalScroll.Value
                                      + this.CharHeight;

                    using (var indicatorPen = new Pen(Color.FromArgb(100, this.FoldingIndicatorColor), 4))
                        e.Graphics.DrawLine(
                            indicatorPen, this.LeftIndent - 5, startFoldingY, this.LeftIndent - 5, endFoldingY);
                }
            }
            //draw hint's brackets
            this.PaintHintBrackets(e.Graphics);
            //draw markers
            foreach (var m in this.visibleMarkers)
            {
                m.Draw(e.Graphics, servicePen);
            }
            //draw caret
            Point car = this.PlaceToPoint(this.Selection.Start);

            if ((this.Focused || this.IsDragDrop) && car.X >= this.LeftIndent && this.CaretVisible)
            {
                int carWidth = (this.IsReplaceMode || this.WideCaret) ? this.CharWidth : 1;
                if (this.WideCaret)
                {
                    using (var brush = new SolidBrush(this.CaretColor)) e.Graphics.FillRectangle(brush, car.X, car.Y, carWidth, this.CharHeight + 1);
                }
                else
                {
                    using (var pen = new Pen(this.CaretColor)) e.Graphics.DrawLine(pen, car.X, car.Y, car.X, car.Y + this.CharHeight);
                }

                var caretRect = new Rectangle(
                    this.HorizontalScroll.Value + car.X,
                    this.VerticalScroll.Value + car.Y,
                    carWidth,
                    this.charHeight + 1);

                if (this.prevCaretRect != caretRect || !this.ShowScrollBars)
                {
                    CreateCaret(this.Handle, 0, carWidth, this.CharHeight + 1);
                    SetCaretPos(car.X, car.Y);
                    ShowCaret(this.Handle);
                }

                this.prevCaretRect = caretRect;
            }
            else
            {
                HideCaret(this.Handle);
                this.prevCaretRect = Rectangle.Empty;
            }

            //draw disabled mask
            if (!this.Enabled)
            {
                using (var brush = new SolidBrush(this.DisabledColor)) e.Graphics.FillRectangle(brush, this.ClientRectangle);
            }

            if (this.MacrosManager.IsRecording)
            {
                this.DrawRecordingHint(e.Graphics);
            }

            if (this.middleClickScrollingActivated)
            {
                this.DrawMiddleClickScrolling(e.Graphics);
            }

            //dispose resources
            servicePen.Dispose();
            changedLineBrush.Dispose();
            indentBrush.Dispose();
            currentLineBrush.Dispose();
            paddingBrush.Dispose();
            //
#if debug
            sw.Stop();
            Console.WriteLine("OnPaint: "+ sw.ElapsedMilliseconds);
#endif
            //
            base.OnPaint(e);
        }

        private Rectangle prevCaretRect;

        private void DrawRecordingHint(Graphics graphics)
        {
            const int w = 75;
            const int h = 13;
            var rect = new Rectangle(this.ClientRectangle.Right - w, this.ClientRectangle.Bottom - h, w, h);
            var iconRect = new Rectangle(-h / 2 + 3, -h / 2 + 3, h - 7, h - 7);
            GraphicsState state = graphics.Save();
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.TranslateTransform(rect.Left + h / 2, rect.Top + h / 2);
            var ts = new TimeSpan(DateTime.Now.Ticks);
            graphics.RotateTransform(180 * (DateTime.Now.Millisecond / 1000f));
            using (var pen = new Pen(Color.Red, 2))
            {
                graphics.DrawArc(pen, iconRect, 0, 90);
                graphics.DrawArc(pen, iconRect, 180, 90);
            }
            graphics.DrawEllipse(Pens.Red, iconRect);
            graphics.Restore(state);
            using (var font = new Font(FontFamily.GenericSansSerif, 8f)) graphics.DrawString("Recording...", font, Brushes.Red, new PointF(rect.Left + h, rect.Top));
            System.Threading.Timer tm = null;
            tm = new System.Threading.Timer(
                (o) =>
                    {
                        Invalidate(rect);
                        tm.Dispose();
                    },
                null,
                200,
                System.Threading.Timeout.Infinite);
        }

        private void DrawTextAreaBorder(Graphics graphics)
        {
            if (this.TextAreaBorder == TextAreaBorderType.None)
            {
                return;
            }

            Rectangle rect = this.TextAreaRect;

            if (this.TextAreaBorder == TextAreaBorderType.Shadow)
            {
                const int shadowSize = 4;
                var rBottom = new Rectangle(rect.Left + shadowSize, rect.Bottom, rect.Width - shadowSize, shadowSize);
                var rCorner = new Rectangle(rect.Right, rect.Bottom, shadowSize, shadowSize);
                var rRight = new Rectangle(rect.Right, rect.Top + shadowSize, shadowSize, rect.Height - shadowSize);

                using (var brush = new SolidBrush(Color.FromArgb(80, this.TextAreaBorderColor)))
                {
                    graphics.FillRectangle(brush, rBottom);
                    graphics.FillRectangle(brush, rRight);
                    graphics.FillRectangle(brush, rCorner);
                }
            }

            using (var pen = new Pen(this.TextAreaBorderColor)) graphics.DrawRectangle(pen, rect);
        }

        private void PaintHintBrackets(Graphics gr)
        {
            foreach (var hint in this.hints)
            {
                Range r = hint.Range.Clone();
                r.Normalize();
                Point p1 = this.PlaceToPoint(r.Start);
                Point p2 = this.PlaceToPoint(r.End);
                if (this.GetVisibleState(r.Start.iLine) != VisibleState.Visible
                    || this.GetVisibleState(r.End.iLine) != VisibleState.Visible)
                {
                    continue;
                }

                using (var pen = new Pen(hint.BorderColor))
                {
                    pen.DashStyle = DashStyle.Dash;
                    if (r.IsEmpty)
                    {
                        p1.Offset(1, -1);
                        gr.DrawLines(pen, new[] { p1, new Point(p1.X, p1.Y + this.charHeight + 2) });
                    }
                    else
                    {
                        p1.Offset(-1, -1);
                        p2.Offset(1, -1);
                        gr.DrawLines(
                            pen,
                            new[]
                                {
                                    new Point(p1.X + this.CharWidth / 2, p1.Y), p1,
                                    new Point(p1.X, p1.Y + this.charHeight + 2),
                                    new Point(p1.X + this.CharWidth / 2, p1.Y + this.charHeight + 2)
                                });
                        gr.DrawLines(
                            pen,
                            new[]
                                {
                                    new Point(p2.X - this.CharWidth / 2, p2.Y), p2,
                                    new Point(p2.X, p2.Y + this.charHeight + 2),
                                    new Point(p2.X - this.CharWidth / 2, p2.Y + this.charHeight + 2)
                                });
                    }
                }
            }
        }

        protected virtual void DrawFoldingLines(PaintEventArgs e, int startLine, int endLine)
        {
            e.Graphics.SmoothingMode = SmoothingMode.None;
            using (var pen = new Pen(Color.FromArgb(200, this.ServiceLinesColor)) { DashStyle = DashStyle.Dot })
                foreach (var iLine in this.foldingPairs)
                {
                    if (iLine.Key < endLine && iLine.Value > startLine)
                    {
                        Line line = this.lines[iLine.Key];
                        int y = this.LineInfos[iLine.Key].startY - this.VerticalScroll.Value + this.CharHeight;
                        y += y % 2;

                        int y2;

                        if (iLine.Value >= this.LinesCount)
                        {
                            y2 = this.LineInfos[this.LinesCount - 1].startY + this.CharHeight
                                 - this.VerticalScroll.Value;
                        }
                        else if (this.LineInfos[iLine.Value].VisibleState == VisibleState.Visible)
                        {
                            int d = 0;
                            int spaceCount = line.StartSpacesCount;
                            if (this.lines[iLine.Value].Count <= spaceCount
                                || this.lines[iLine.Value][spaceCount].c == ' ')
                            {
                                d = this.CharHeight;
                            }
                            y2 = this.LineInfos[iLine.Value].startY - this.VerticalScroll.Value + d;
                        }
                        else
                        {
                            continue;
                        }

                        int x = this.LeftIndent + this.Paddings.Left + line.StartSpacesCount * this.CharWidth
                                - this.HorizontalScroll.Value;
                        if (x >= this.LeftIndent + this.Paddings.Left)
                        {
                            e.Graphics.DrawLine(
                                pen, x, y >= 0 ? y : 0, x, y2 < this.ClientSize.Height ? y2 : this.ClientSize.Height);
                        }
                    }
                }
        }

        private void DrawLineChars(
            Graphics gr, int firstChar, int lastChar, int iLine, int iWordWrapLine, int startX, int y)
        {
            Line line = this.lines[iLine];
            LineInfo lineInfo = this.LineInfos[iLine];
            int from = lineInfo.GetWordWrapStringStartPosition(iWordWrapLine);
            int to = lineInfo.GetWordWrapStringFinishPosition(iWordWrapLine, line);

            lastChar = Math.Min(to - from, lastChar);

            gr.SmoothingMode = SmoothingMode.AntiAlias;

            //folded block ?
            if (lineInfo.VisibleState == VisibleState.StartOfHiddenBlock)
            {
                //rendering by FoldedBlockStyle
                this.FoldedBlockStyle.Draw(
                    gr,
                    new Point(startX + firstChar * this.CharWidth, y),
                    new Range(this, from + firstChar, iLine, from + lastChar + 1, iLine));
            }
            else
            {
                //render by custom styles
                var currentStyleIndex = StyleIndex.None;
                int iLastFlushedChar = firstChar - 1;

                for (int iChar = firstChar; iChar <= lastChar; iChar++)
                {
                    StyleIndex style = line[from + iChar].style;
                    if (currentStyleIndex != style)
                    {
                        this.FlushRendering(
                            gr,
                            currentStyleIndex,
                            new Point(startX + (iLastFlushedChar + 1) * this.CharWidth, y),
                            new Range(this, from + iLastFlushedChar + 1, iLine, from + iChar, iLine));
                        iLastFlushedChar = iChar - 1;
                        currentStyleIndex = style;
                    }
                }
                this.FlushRendering(
                    gr,
                    currentStyleIndex,
                    new Point(startX + (iLastFlushedChar + 1) * this.CharWidth, y),
                    new Range(this, from + iLastFlushedChar + 1, iLine, from + lastChar + 1, iLine));
            }

            //draw selection
            if (this.SelectionHighlightingForLineBreaksEnabled && iWordWrapLine == lineInfo.WordWrapStringsCount - 1)
            {
                lastChar++; //draw selection for CR
            }
            if (!this.Selection.IsEmpty && lastChar >= firstChar)
            {
                gr.SmoothingMode = SmoothingMode.None;
                var textRange = new Range(this, from + firstChar, iLine, from + lastChar + 1, iLine);
                textRange = this.Selection.GetIntersectionWith(textRange);
                if (textRange != null && this.SelectionStyle != null)
                {
                    this.SelectionStyle.Draw(
                        gr, new Point(startX + (textRange.Start.iChar - from) * this.CharWidth, 1 + y), textRange);
                }
            }
        }

        private void FlushRendering(Graphics gr, StyleIndex styleIndex, Point pos, Range range)
        {
            if (range.End > range.Start)
            {
                int mask = 1;
                bool hasTextStyle = false;
                for (int i = 0; i < this.Styles.Length; i++)
                {
                    if (this.Styles[i] != null && ((int)styleIndex & mask) != 0)
                    {
                        Style style = this.Styles[i];
                        bool isTextStyle = style is TextStyle;
                        if (!hasTextStyle || !isTextStyle || this.AllowSeveralTextStyleDrawing)
                        {
                            //cancelling secondary rendering by TextStyle
                            style.Draw(gr, pos, range); //rendering
                        }
                        hasTextStyle |= isTextStyle;
                    }
                    mask = mask << 1;
                }
                //draw by default renderer
                if (!hasTextStyle)
                {
                    this.DefaultStyle.Draw(gr, pos, range);
                }
            }
        }

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            this.mouseIsDrag = false;
            this.mouseIsDragDrop = false;
            this.draggedRange = null;
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            this.isLineSelect = false;

            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (this.mouseIsDragDrop)
                {
                    this.OnMouseClickText(e);
                }
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (this.middleClickScrollingActivated)
            {
                this.DeactivateMiddleClickScrollingMode();
                this.mouseIsDrag = false;
                if (e.Button == System.Windows.Forms.MouseButtons.Middle)
                {
                    this.RestoreScrollsAfterMiddleClickScrollingMode();
                }
                return;
            }

            this.MacrosManager.IsRecording = false;

            this.Select();
            this.ActiveControl = null;

            if (e.Button == MouseButtons.Left)
            {
                VisualMarker marker = this.FindVisualMarkerForPoint(e.Location);
                //click on marker
                if (marker != null)
                {
                    this.mouseIsDrag = false;
                    this.mouseIsDragDrop = false;
                    this.draggedRange = null;
                    this.OnMarkerClick(e, marker);
                    return;
                }

                this.mouseIsDrag = true;
                this.mouseIsDragDrop = false;
                this.draggedRange = null;
                this.isLineSelect = (e.Location.X < this.LeftIndentLine);

                if (!this.isLineSelect)
                {
                    Place p = this.PointToPlace(e.Location);

                    if (e.Clicks == 2)
                    {
                        this.mouseIsDrag = false;
                        this.mouseIsDragDrop = false;
                        this.draggedRange = null;

                        this.SelectWord(p);
                        return;
                    }

                    if (this.Selection.IsEmpty || !this.Selection.Contains(p) || this[p.iLine].Count <= p.iChar
                        || this.ReadOnly)
                    {
                        this.OnMouseClickText(e);
                    }
                    else
                    {
                        this.mouseIsDragDrop = true;
                        this.mouseIsDrag = false;
                    }
                }
                else
                {
                    this.CheckAndChangeSelectionType();

                    this.Selection.BeginUpdate();
                    //select whole line
                    int iLine = this.PointToPlaceSimple(e.Location).iLine;
                    this.lineSelectFrom = iLine;
                    this.Selection.Start = new Place(0, iLine);
                    this.Selection.End = new Place(this.GetLineLength(iLine), iLine);
                    this.Selection.EndUpdate();
                    this.Invalidate();
                }
            }
            else if (e.Button == MouseButtons.Middle)
            {
                this.ActivateMiddleClickScrollingMode(e);
            }
        }

        private void OnMouseClickText(MouseEventArgs e)
        {
            //click on text
            Place oldEnd = this.Selection.End;
            this.Selection.BeginUpdate();

            if (this.Selection.ColumnSelectionMode)
            {
                this.Selection.Start = this.PointToPlaceSimple(e.Location);
                this.Selection.ColumnSelectionMode = true;
            }
            else
            {
                if (this.VirtualSpace)
                {
                    this.Selection.Start = this.PointToPlaceSimple(e.Location);
                }
                else
                {
                    this.Selection.Start = this.PointToPlace(e.Location);
                }
            }

            if ((this.lastModifiers & Keys.Shift) != 0)
            {
                this.Selection.End = oldEnd;
            }

            this.CheckAndChangeSelectionType();

            this.Selection.EndUpdate();
            this.Invalidate();
            return;
        }

        protected virtual void CheckAndChangeSelectionType()
        {
            //change selection type to ColumnSelectionMode
            if ((ModifierKeys & Keys.Alt) != 0 && !this.WordWrap)
            {
                this.Selection.ColumnSelectionMode = true;
            }
            else
                //change selection type to Range
            {
                this.Selection.ColumnSelectionMode = false;
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            this.Invalidate();

            if (this.lastModifiers == Keys.Control)
            {
                this.ChangeFontSize(2 * Math.Sign(e.Delta));
                ((HandledMouseEventArgs)e).Handled = true;
            }
            else if (this.VerticalScroll.Visible || !this.ShowScrollBars)
            {
                //base.OnMouseWheel(e);

                // Determine scoll offset
                int mouseWheelScrollLinesSetting = GetControlPanelWheelScrollLinesValue();

                this.DoScrollVertical(mouseWheelScrollLinesSetting, e.Delta);

                ((HandledMouseEventArgs)e).Handled = true;
            }

            this.DeactivateMiddleClickScrollingMode();
        }

        private void DoScrollVertical(int countLines, int direction)
        {
            int numberOfVisibleLines = this.ClientSize.Height / this.CharHeight;

            int offset;
            if ((countLines == -1) || (countLines > numberOfVisibleLines))
            {
                offset = this.CharHeight * numberOfVisibleLines;
            }
            else
            {
                offset = this.CharHeight * countLines;
            }

            int newScrollPos = this.VerticalScroll.Value - Math.Sign(direction) * offset;

            var ea = new ScrollEventArgs(
                direction > 0 ? ScrollEventType.SmallDecrement : ScrollEventType.SmallIncrement,
                this.VerticalScroll.Value,
                newScrollPos,
                ScrollOrientation.VerticalScroll);

            this.OnScroll(ea);
        }

        /// <summary>
        /// 	Gets the value for the system control panel mouse wheel scroll settings.
        /// 	The value returns the number of lines that shall be scolled if the user turns the mouse wheet one step.
        /// </summary>
        /// <remarks>
        /// 	This methods gets the "WheelScrollLines" value our from the registry key "HKEY_CURRENT_USER\Control Panel\Desktop".
        /// 	If the value of this option is 0, the screen will not scroll when the mouse wheel is turned.
        /// 	If the value of this option is -1 or is greater than the number of lines visible in the window,
        /// 	the screen will scroll up or down by one page.
        /// </remarks>
        /// <returns> Number of lines to scrol l when the mouse wheel is turned </returns>
        private static int GetControlPanelWheelScrollLinesValue()
        {
            try
            {
                using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", false))
                {
                    return Convert.ToInt32(key.GetValue("WheelScrollLines"));
                }
            }
            catch
            {
                // Use default value
                return 1;
            }
        }

        public void ChangeFontSize(int step)
        {
            float points = this.Font.SizeInPoints;
            using (Graphics gr = Graphics.FromHwnd(this.Handle))
            {
                float dpi = gr.DpiY;
                float newPoints = points + step * 72f / dpi;
                if (newPoints < 1f)
                {
                    return;
                }
                float k = newPoints / this.originalFont.SizeInPoints;
                this.Zoom = (int)(100 * k);
            }
        }

        /// <summary>
        /// 	Zooming (in percentages)
        /// </summary>
        [Browsable(false)]
        public int Zoom
        {
            get
            {
                return this.zoom;
            }
            set
            {
                this.zoom = value;
                this.DoZoom(this.zoom / 100f);
                this.OnZoomChanged();
            }
        }

        protected virtual void OnZoomChanged()
        {
            if (this.ZoomChanged != null)
            {
                this.ZoomChanged(this, EventArgs.Empty);
            }
        }

        private void DoZoom(float koeff)
        {
            //remmber first displayed line
            int iLine = this.YtoLineIndex(this.VerticalScroll.Value);
            //
            float points = this.originalFont.SizeInPoints;
            points *= koeff;

            if (points < 1f || points > 300f)
            {
                return;
            }

            Font oldFont = this.Font;
            this.SetFont(new Font(this.Font.FontFamily, points, this.Font.Style, GraphicsUnit.Point));
            oldFont.Dispose();

            this.NeedRecalc(true);

            //restore first displayed line
            if (iLine < this.LinesCount)
            {
                this.VerticalScroll.Value = Math.Min(this.VerticalScroll.Maximum, this.LineInfos[iLine].startY);
            }
            this.UpdateScrollbars();
            //
            this.Invalidate();
            this.OnVisibleRangeChanged();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);

            this.CancelToolTip();
        }

        protected Range draggedRange;

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (this.middleClickScrollingActivated)
            {
                return;
            }

            if (this.lastMouseCoord != e.Location)
            {
                //restart tooltip timer
                this.CancelToolTip();
                this.timer3.Start();
            }
            this.lastMouseCoord = e.Location;

            if (e.Button == MouseButtons.Left && this.mouseIsDragDrop)
            {
                this.draggedRange = this.Selection.Clone();
                this.DoDragDrop(this.SelectedText, DragDropEffects.Copy);
                this.draggedRange = null;
                return;
            }

            if (e.Button == MouseButtons.Left && this.mouseIsDrag)
            {
                Place place;
                if (this.Selection.ColumnSelectionMode || this.VirtualSpace)
                {
                    place = this.PointToPlaceSimple(e.Location);
                }
                else
                {
                    place = this.PointToPlace(e.Location);
                }

                if (this.isLineSelect)
                {
                    this.Selection.BeginUpdate();

                    int iLine = place.iLine;
                    if (iLine < this.lineSelectFrom)
                    {
                        this.Selection.Start = new Place(0, iLine);
                        this.Selection.End = new Place(this.GetLineLength(this.lineSelectFrom), this.lineSelectFrom);
                    }
                    else
                    {
                        this.Selection.Start = new Place(this.GetLineLength(iLine), iLine);
                        this.Selection.End = new Place(0, this.lineSelectFrom);
                    }

                    this.Selection.EndUpdate();
                    this.DoCaretVisible();
                    this.HorizontalScroll.Value = 0;
                    this.UpdateScrollbars();
                    this.Invalidate();
                }
                else if (place != this.Selection.Start)
                {
                    Place oldEnd = this.Selection.End;
                    this.Selection.BeginUpdate();
                    if (this.Selection.ColumnSelectionMode)
                    {
                        this.Selection.Start = place;
                        this.Selection.ColumnSelectionMode = true;
                    }
                    else
                    {
                        this.Selection.Start = place;
                    }
                    this.Selection.End = oldEnd;
                    this.Selection.EndUpdate();
                    this.DoCaretVisible();
                    this.Invalidate();
                    return;
                }
            }

            VisualMarker marker = this.FindVisualMarkerForPoint(e.Location);
            if (marker != null)
            {
                base.Cursor = marker.Cursor;
            }
            else
            {
                if (e.Location.X < this.LeftIndentLine || this.isLineSelect)
                {
                    base.Cursor = Cursors.Arrow;
                }
                else
                {
                    base.Cursor = this.defaultCursor;
                }
            }
        }

        private void CancelToolTip()
        {
            this.timer3.Stop();
            if (this.ToolTip != null && !string.IsNullOrEmpty(this.ToolTip.GetToolTip(this)))
            {
                this.ToolTip.Hide(this);
                this.ToolTip.SetToolTip(this, null);
            }
        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);

            VisualMarker m = this.FindVisualMarkerForPoint(e.Location);
            if (m != null)
            {
                this.OnMarkerDoubleClick(m);
            }
        }

        private void SelectWord(Place p)
        {
            int fromX = p.iChar;
            int toX = p.iChar;

            for (int i = p.iChar; i < this.lines[p.iLine].Count; i++)
            {
                char c = this.lines[p.iLine][i].c;
                if (char.IsLetterOrDigit(c) || c == '_')
                {
                    toX = i + 1;
                }
                else
                {
                    break;
                }
            }

            for (int i = p.iChar - 1; i >= 0; i--)
            {
                char c = this.lines[p.iLine][i].c;
                if (char.IsLetterOrDigit(c) || c == '_')
                {
                    fromX = i;
                }
                else
                {
                    break;
                }
            }

            this.Selection = new Range(this, toX, p.iLine, fromX, p.iLine);
        }

        private int YtoLineIndex(int y)
        {
            int i = this.LineInfos.BinarySearch(new LineInfo(-10), new LineYComparer(y));
            i = i < 0 ? -i - 2 : i;
            if (i < 0)
            {
                return 0;
            }
            if (i > this.lines.Count - 1)
            {
                return this.lines.Count - 1;
            }
            return i;
        }

        /// <summary>
        /// 	Gets nearest line and char position from coordinates
        /// </summary>
        /// <param name="point"> Point </param>
        /// <returns> Line and char position </returns>
        public Place PointToPlace(Point point)
        {
#if debug
            var sw = Stopwatch.StartNew();
            #endif
            point.Offset(this.HorizontalScroll.Value, this.VerticalScroll.Value);
            point.Offset(-this.LeftIndent - this.Paddings.Left, 0);
            int iLine = this.YtoLineIndex(point.Y);
            if (iLine < 0)
            {
                return Place.Empty;
            }

            int y = 0;

            for (; iLine < this.lines.Count; iLine++)
            {
                y = this.LineInfos[iLine].startY + this.LineInfos[iLine].WordWrapStringsCount * this.CharHeight;
                if (y > point.Y && this.LineInfos[iLine].VisibleState == VisibleState.Visible)
                {
                    break;
                }
            }
            if (iLine >= this.lines.Count)
            {
                iLine = this.lines.Count - 1;
            }
            if (this.LineInfos[iLine].VisibleState != VisibleState.Visible)
            {
                iLine = this.FindPrevVisibleLine(iLine);
            }
            //
            int iWordWrapLine = this.LineInfos[iLine].WordWrapStringsCount;
            do
            {
                iWordWrapLine--;
                y -= this.CharHeight;
            }
            while (y > point.Y);
            if (iWordWrapLine < 0)
            {
                iWordWrapLine = 0;
            }
            //
            int start = this.LineInfos[iLine].GetWordWrapStringStartPosition(iWordWrapLine);
            int finish = this.LineInfos[iLine].GetWordWrapStringFinishPosition(iWordWrapLine, this.lines[iLine]);
            var x = (int)Math.Round((float)point.X / this.CharWidth);
            if (iWordWrapLine > 0)
            {
                x -= this.LineInfos[iLine].wordWrapIndent;
            }

            x = x < 0 ? start : start + x;
            if (x > finish)
            {
                x = finish + 1;
            }
            if (x > this.lines[iLine].Count)
            {
                x = this.lines[iLine].Count;
            }

#if debug
            Console.WriteLine("PointToPlace: " + sw.ElapsedMilliseconds);
#endif

            return new Place(x, iLine);
        }

        private Place PointToPlaceSimple(Point point)
        {
            point.Offset(this.HorizontalScroll.Value, this.VerticalScroll.Value);
            point.Offset(-this.LeftIndent - this.Paddings.Left, 0);
            int iLine = this.YtoLineIndex(point.Y);
            var x = (int)Math.Round((float)point.X / this.CharWidth);
            if (x < 0)
            {
                x = 0;
            }
            return new Place(x, iLine);
        }

        /// <summary>
        /// 	Gets nearest absolute text position for given point
        /// </summary>
        /// <param name="point"> Point </param>
        /// <returns> Position </returns>
        public int PointToPosition(Point point)
        {
            return this.PlaceToPosition(this.PointToPlace(point));
        }

        /// <summary>
        /// 	Fires TextChanging event
        /// </summary>
        public virtual void OnTextChanging(ref string text)
        {
            this.ClearBracketsPositions();

            if (this.TextChanging != null)
            {
                var args = new TextChangingEventArgs { InsertingText = text };
                this.TextChanging(this, args);
                text = args.InsertingText;
                if (args.Cancel)
                {
                    text = string.Empty;
                }
            }
        }

        public virtual void OnTextChanging()
        {
            string temp = null;
            this.OnTextChanging(ref temp);
        }

        /// <summary>
        /// 	Fires TextChanged event
        /// </summary>
        public virtual void OnTextChanged()
        {
            var r = new Range(this);
            r.SelectAll();
            this.OnTextChanged(new TextChangedEventArgs(r));
        }

        /// <summary>
        /// 	Fires TextChanged event
        /// </summary>
        public virtual void OnTextChanged(int fromLine, int toLine)
        {
            var r = new Range(this);
            r.Start = new Place(0, Math.Min(fromLine, toLine));
            r.End = new Place(this.lines[Math.Max(fromLine, toLine)].Count, Math.Max(fromLine, toLine));
            this.OnTextChanged(new TextChangedEventArgs(r));
        }

        /// <summary>
        /// 	Fires TextChanged event
        /// </summary>
        public virtual void OnTextChanged(Range r)
        {
            this.OnTextChanged(new TextChangedEventArgs(r));
        }

        /// <summary>
        /// 	Call this method before multiple text changing
        /// </summary>
        public void BeginUpdate()
        {
            if (this.updating == 0)
            {
                this.updatingRange = null;
            }
            this.updating++;
        }

        /// <summary>
        /// 	Call this method after multiple text changing
        /// </summary>
        public void EndUpdate()
        {
            this.updating--;

            if (this.updating == 0 && this.updatingRange != null)
            {
                this.updatingRange.Expand();
                this.OnTextChanged(this.updatingRange);
            }
        }

        /// <summary>
        /// 	Fires TextChanged event
        /// </summary>
        protected virtual void OnTextChanged(TextChangedEventArgs args)
        {
            //
            args.ChangedRange.Normalize();
            //
            if (this.updating > 0)
            {
                if (this.updatingRange == null)
                {
                    this.updatingRange = args.ChangedRange.Clone();
                }
                else
                {
                    if (this.updatingRange.Start.iLine > args.ChangedRange.Start.iLine)
                    {
                        this.updatingRange.Start = new Place(0, args.ChangedRange.Start.iLine);
                    }
                    if (this.updatingRange.End.iLine < args.ChangedRange.End.iLine)
                    {
                        this.updatingRange.End = new Place(
                            this.lines[args.ChangedRange.End.iLine].Count, args.ChangedRange.End.iLine);
                    }
                    this.updatingRange = this.updatingRange.GetIntersectionWith(this.Range);
                }
                return;
            }
            //
#if debug
            var sw = Stopwatch.StartNew();
            #endif
            this.CancelToolTip();
            this.ClearHints();
            this.IsChanged = true;
            this.TextVersion++;
            this.MarkLinesAsChanged(args.ChangedRange);
            this.ClearFoldingState(args.ChangedRange);
            //
            if (this.wordWrap)
            {
                this.RecalcWordWrap(args.ChangedRange.Start.iLine, args.ChangedRange.End.iLine);
            }
            //
            base.OnTextChanged(args);

            //dalayed event stuffs
            if (this.delayedTextChangedRange == null)
            {
                this.delayedTextChangedRange = args.ChangedRange.Clone();
            }
            else
            {
                this.delayedTextChangedRange = this.delayedTextChangedRange.GetUnionWith(args.ChangedRange);
            }

            this.needRiseTextChangedDelayed = true;
            this.ResetTimer(this.timer2);
            //
            this.OnSyntaxHighlight(args);
            //
            if (this.TextChanged != null)
            {
                this.TextChanged(this, args);
            }
            //
            if (this.BindingTextChanged != null)
            {
                this.BindingTextChanged(this, EventArgs.Empty);
            }
            //
            base.OnTextChanged(EventArgs.Empty);
            //
#if debug
            Console.WriteLine("OnTextChanged: " + sw.ElapsedMilliseconds);
#endif

            this.OnVisibleRangeChanged();
        }

        /// <summary>
        /// 	Clears folding state for range of text
        /// </summary>
        private void ClearFoldingState(Range range)
        {
            for (int iLine = range.Start.iLine; iLine <= range.End.iLine; iLine++)
            {
                if (iLine >= 0 && iLine < this.lines.Count)
                {
                    this.FoldedBlocks.Remove(this[iLine].UniqueId);
                }
            }
        }

        private void MarkLinesAsChanged(Range range)
        {
            for (int iLine = range.Start.iLine; iLine <= range.End.iLine; iLine++)
            {
                if (iLine >= 0 && iLine < this.lines.Count)
                {
                    this.lines[iLine].IsChanged = true;
                }
            }
        }

        /// <summary>
        /// 	Fires SelectionChanged event
        /// </summary>
        public virtual void OnSelectionChanged()
        {
#if debug
            var sw = Stopwatch.StartNew();
            #endif
            //find folding markers for highlighting
            if (this.HighlightFoldingIndicator)
            {
                this.HighlightFoldings();
            }
            //
            this.needRiseSelectionChangedDelayed = true;
            this.ResetTimer(this.timer);

            if (this.SelectionChanged != null)
            {
                this.SelectionChanged(this, new EventArgs());
            }

#if debug
            Console.WriteLine("OnSelectionChanged: "+ sw.ElapsedMilliseconds);
#endif
        }

        //find folding markers for highlighting
        private void HighlightFoldings()
        {
            if (this.LinesCount == 0)
            {
                return;
            }
            //
            int prevStartFoldingLine = this.startFoldingLine;
            int prevEndFoldingLine = this.endFoldingLine;
            //
            this.startFoldingLine = -1;
            this.endFoldingLine = -1;
            int counter = 0;
            for (int i = this.Selection.Start.iLine;
                 i >= Math.Max(this.Selection.Start.iLine - maxLinesForFolding, 0);
                 i--)
            {
                bool hasStartMarker = this.lines.LineHasFoldingStartMarker(i);
                bool hasEndMarker = this.lines.LineHasFoldingEndMarker(i);

                if (hasEndMarker && hasStartMarker)
                {
                    continue;
                }

                if (hasStartMarker)
                {
                    counter--;
                    if (counter == -1) //found start folding
                    {
                        this.startFoldingLine = i;
                        break;
                    }
                }
                if (hasEndMarker && i != this.Selection.Start.iLine)
                {
                    counter++;
                }
            }
            if (this.startFoldingLine >= 0)
            {
                //find end of block
                this.endFoldingLine = this.FindEndOfFoldingBlock(this.startFoldingLine, maxLinesForFolding);
                if (this.endFoldingLine == this.startFoldingLine)
                {
                    this.endFoldingLine = -1;
                }
            }

            if (this.startFoldingLine != prevStartFoldingLine || this.endFoldingLine != prevEndFoldingLine)
            {
                this.OnFoldingHighlightChanged();
            }
        }

        protected virtual void OnFoldingHighlightChanged()
        {
            if (this.FoldingHighlightChanged != null)
            {
                this.FoldingHighlightChanged(this, EventArgs.Empty);
            }
        }

        protected override void OnGotFocus(EventArgs e)
        {
            this.SetAsCurrentTB();
            base.OnGotFocus(e);
            this.Invalidate();
        }

        protected override void OnLostFocus(EventArgs e)
        {
            this.lastModifiers = Keys.None;
            this.DeactivateMiddleClickScrollingMode();
            base.OnLostFocus(e);
            this.Invalidate();
        }

        /// <summary>
        /// 	Gets absolute text position from line and char position
        /// </summary>
        /// <param name="point"> Line and char position </param>
        /// <returns> Point of char </returns>
        public int PlaceToPosition(Place point)
        {
            if (point.iLine < 0 || point.iLine >= this.lines.Count
                || point.iChar >= this.lines[point.iLine].Count + Environment.NewLine.Length)
            {
                return -1;
            }

            int result = 0;
            for (int i = 0; i < point.iLine; i++)
            {
                result += this.lines[i].Count + Environment.NewLine.Length;
            }
            result += point.iChar;

            return result;
        }

        /// <summary>
        /// 	Gets line and char position from absolute text position
        /// </summary>
        public Place PositionToPlace(int pos)
        {
            if (pos < 0)
            {
                return new Place(0, 0);
            }

            for (int i = 0; i < this.lines.Count; i++)
            {
                int lineLength = this.lines[i].Count + Environment.NewLine.Length;
                if (pos < this.lines[i].Count)
                {
                    return new Place(pos, i);
                }
                if (pos < lineLength)
                {
                    return new Place(this.lines[i].Count, i);
                }

                pos -= lineLength;
            }

            if (this.lines.Count > 0)
            {
                return new Place(this.lines[this.lines.Count - 1].Count, this.lines.Count - 1);
            }
            else
            {
                return new Place(0, 0);
            }
            //throw new ArgumentOutOfRangeException("Position out of range");
        }

        /// <summary>
        /// 	Gets absolute char position from char position
        /// </summary>
        public Point PositionToPoint(int pos)
        {
            return this.PlaceToPoint(this.PositionToPlace(pos));
        }

        /// <summary>
        /// 	Gets point for given line and char position
        /// </summary>
        /// <param name="place"> Line and char position </param>
        /// <returns> Coordiantes </returns>
        public Point PlaceToPoint(Place place)
        {
            if (place.iLine >= this.LineInfos.Count)
            {
                return new Point();
            }
            int y = this.LineInfos[place.iLine].startY;
            //
            int iWordWrapIndex = this.LineInfos[place.iLine].GetWordWrapStringIndex(place.iChar);
            y += iWordWrapIndex * this.CharHeight;
            int x = (place.iChar - this.LineInfos[place.iLine].GetWordWrapStringStartPosition(iWordWrapIndex))
                    * this.CharWidth;
            if (iWordWrapIndex > 0)
            {
                x += this.LineInfos[place.iLine].wordWrapIndent * this.CharWidth;
            }
            //
            y = y - this.VerticalScroll.Value;
            x = this.LeftIndent + this.Paddings.Left + x - this.HorizontalScroll.Value;

            return new Point(x, y);
        }

        /// <summary>
        /// 	Get range of text
        /// </summary>
        /// <param name="fromPos"> Absolute start position </param>
        /// <param name="toPos"> Absolute finish position </param>
        /// <returns> Range </returns>
        public Range GetRange(int fromPos, int toPos)
        {
            var sel = new Range(this);
            sel.Start = this.PositionToPlace(fromPos);
            sel.End = this.PositionToPlace(toPos);
            return sel;
        }

        /// <summary>
        /// 	Get range of text
        /// </summary>
        /// <param name="fromPlace"> Line and char position </param>
        /// <param name="toPlace"> Line and char position </param>
        /// <returns> Range </returns>
        public Range GetRange(Place fromPlace, Place toPlace)
        {
            return new Range(this, fromPlace, toPlace);
        }

        /// <summary>
        /// 	Finds ranges for given regex pattern
        /// </summary>
        /// <param name="regexPattern"> Regex pattern </param>
        /// <returns> Enumeration of ranges </returns>
        public IEnumerable<Range> GetRanges(string regexPattern)
        {
            var range = new Range(this);
            range.SelectAll();
            //
            foreach (var r in range.GetRanges(regexPattern, RegexOptions.None))
            {
                yield return r;
            }
        }

        /// <summary>
        /// 	Finds ranges for given regex pattern
        /// </summary>
        /// <param name="regexPattern"> Regex pattern </param>
        /// <returns> Enumeration of ranges </returns>
        public IEnumerable<Range> GetRanges(string regexPattern, RegexOptions options)
        {
            var range = new Range(this);
            range.SelectAll();
            //
            foreach (var r in range.GetRanges(regexPattern, options))
            {
                yield return r;
            }
        }

        /// <summary>
        /// 	Get text of given line
        /// </summary>
        /// <param name="iLine"> Line index </param>
        /// <returns> Text </returns>
        public string GetLineText(int iLine)
        {
            if (iLine < 0 || iLine >= this.lines.Count)
            {
                throw new ArgumentOutOfRangeException("Line index out of range");
            }
            var sb = new StringBuilder(this.lines[iLine].Count);
            foreach (var c in this.lines[iLine])
            {
                sb.Append(c.c);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 	Exapnds folded block
        /// </summary>
        /// <param name="iLine"> Start line </param>
        public virtual void ExpandFoldedBlock(int iLine)
        {
            if (iLine < 0 || iLine >= this.lines.Count)
            {
                throw new ArgumentOutOfRangeException("Line index out of range");
            }
            //find all hidden lines afetr iLine
            int end = iLine;
            for (; end < this.LinesCount - 1; end++)
            {
                if (this.LineInfos[end + 1].VisibleState != VisibleState.Hidden)
                {
                    break;
                }
            }

            this.ExpandBlock(iLine, end);

            this.FoldedBlocks.Remove(this[iLine].UniqueId); //remove folded state for this line
            this.AdjustFolding();
        }

        /// <summary>
        /// 	Collapse folding blocks using FoldedBlocks dictionary.
        /// </summary>
        public virtual void AdjustFolding()
        {
            //collapse folded blocks
            for (int iLine = 0; iLine < this.LinesCount; iLine++)
            {
                if (this.LineInfos[iLine].VisibleState == VisibleState.Visible)
                {
                    if (this.FoldedBlocks.ContainsKey(this[iLine].UniqueId))
                    {
                        this.CollapseFoldingBlock(iLine);
                    }
                }
            }
        }

        /// <summary>
        /// 	Expand collapsed block
        /// </summary>
        public virtual void ExpandBlock(int fromLine, int toLine)
        {
            int from = Math.Min(fromLine, toLine);
            int to = Math.Max(fromLine, toLine);
            for (int i = from; i <= to; i++)
            {
                this.SetVisibleState(i, VisibleState.Visible);
            }
            this.needRecalc = true;

            this.Invalidate();
            this.OnVisibleRangeChanged();
        }

        /// <summary>
        /// 	Expand collapsed block
        /// </summary>
        /// <param name="iLine"> Any line inside collapsed block </param>
        public void ExpandBlock(int iLine)
        {
            if (this.LineInfos[iLine].VisibleState == VisibleState.Visible)
            {
                return;
            }

            for (int i = iLine; i < this.LinesCount; i++)
            {
                if (this.LineInfos[i].VisibleState == VisibleState.Visible)
                {
                    break;
                }
                else
                {
                    this.SetVisibleState(i, VisibleState.Visible);
                    this.needRecalc = true;
                }
            }

            for (int i = iLine - 1; i >= 0; i--)
            {
                if (this.LineInfos[i].VisibleState == VisibleState.Visible)
                {
                    break;
                }
                else
                {
                    this.SetVisibleState(i, VisibleState.Visible);
                    this.needRecalc = true;
                }
            }

            this.Invalidate();
            this.OnVisibleRangeChanged();
        }

        /// <summary>
        /// 	Collapses all folding blocks
        /// </summary>
        public virtual void CollapseAllFoldingBlocks()
        {
            for (int i = 0; i < this.LinesCount; i++)
            {
                if (this.lines.LineHasFoldingStartMarker(i))
                {
                    int iFinish = this.FindEndOfFoldingBlock(i);
                    if (iFinish >= 0)
                    {
                        this.CollapseBlock(i, iFinish);
                        i = iFinish;
                    }
                }
            }

            this.OnVisibleRangeChanged();
            this.UpdateScrollbars();
        }

        /// <summary>
        /// 	Exapnds all folded blocks
        /// </summary>
        /// <param name="iLine"> </param>
        public virtual void ExpandAllFoldingBlocks()
        {
            for (int i = 0; i < this.LinesCount; i++)
            {
                this.SetVisibleState(i, VisibleState.Visible);
            }

            this.FoldedBlocks.Clear();

            this.OnVisibleRangeChanged();
            this.Invalidate();
            this.UpdateScrollbars();
        }

        /// <summary>
        /// 	Collapses folding block
        /// </summary>
        /// <param name="iLine"> Start folding line </param>
        public virtual void CollapseFoldingBlock(int iLine)
        {
            if (iLine < 0 || iLine >= this.lines.Count)
            {
                throw new ArgumentOutOfRangeException("Line index out of range");
            }
            if (string.IsNullOrEmpty(this.lines[iLine].FoldingStartMarker))
            {
                throw new ArgumentOutOfRangeException("This line is not folding start line");
            }
            //find end of block
            int i = this.FindEndOfFoldingBlock(iLine);
            //collapse
            if (i >= 0)
            {
                this.CollapseBlock(iLine, i);
                int id = this[iLine].UniqueId;
                this.FoldedBlocks[id] = id; //add folded state for line
            }
        }

        private int FindEndOfFoldingBlock(int iStartLine)
        {
            return this.FindEndOfFoldingBlock(iStartLine, int.MaxValue);
        }

        protected virtual int FindEndOfFoldingBlock(int iStartLine, int maxLines)
        {
            //find end of block
            int i;
            string marker = this.lines[iStartLine].FoldingStartMarker;
            var stack = new Stack<string>();

            switch (this.FindEndOfFoldingBlockStrategy)
            {
                case FindEndOfFoldingBlockStrategy.Strategy1:
                    for (i = iStartLine /*+1*/; i < this.LinesCount; i++)
                    {
                        if (this.lines.LineHasFoldingStartMarker(i))
                        {
                            stack.Push(this.lines[i].FoldingStartMarker);
                        }

                        if (this.lines.LineHasFoldingEndMarker(i))
                        {
                            string m = this.lines[i].FoldingEndMarker;
                            while (stack.Count > 0 && stack.Pop() != m)
                            {
                                ;
                            }
                            if (stack.Count == 0)
                            {
                                return i;
                            }
                        }

                        maxLines--;
                        if (maxLines < 0)
                        {
                            return i;
                        }
                    }
                    break;

                case FindEndOfFoldingBlockStrategy.Strategy2:
                    for (i = iStartLine /*+1*/; i < this.LinesCount; i++)
                    {
                        if (this.lines.LineHasFoldingEndMarker(i))
                        {
                            string m = this.lines[i].FoldingEndMarker;
                            while (stack.Count > 0 && stack.Pop() != m)
                            {
                                ;
                            }
                            if (stack.Count == 0)
                            {
                                return i;
                            }
                        }

                        if (this.lines.LineHasFoldingStartMarker(i))
                        {
                            stack.Push(this.lines[i].FoldingStartMarker);
                        }

                        maxLines--;
                        if (maxLines < 0)
                        {
                            return i;
                        }
                    }
                    break;
            }

            //return -1;
            return this.LinesCount - 1;
        }

        /// <summary>
        /// 	Start foilding marker for the line
        /// </summary>
        public string GetLineFoldingStartMarker(int iLine)
        {
            if (this.lines.LineHasFoldingStartMarker(iLine))
            {
                return this.lines[iLine].FoldingStartMarker;
            }
            return null;
        }

        /// <summary>
        /// 	End foilding marker for the line
        /// </summary>
        public string GetLineFoldingEndMarker(int iLine)
        {
            if (this.lines.LineHasFoldingEndMarker(iLine))
            {
                return this.lines[iLine].FoldingEndMarker;
            }
            return null;
        }

        protected virtual void RecalcFoldingLines()
        {
            if (!this.needRecalcFoldingLines)
            {
                return;
            }
            this.needRecalcFoldingLines = false;
            if (!this.ShowFoldingLines)
            {
                return;
            }

            this.foldingPairs.Clear();
            //
            Range range = this.VisibleRange;
            int startLine = Math.Max(range.Start.iLine - maxLinesForFolding, 0);
            int endLine = Math.Min(range.End.iLine + maxLinesForFolding, Math.Max(range.End.iLine, this.LinesCount - 1));
            var stack = new Stack<int>();
            for (int i = startLine; i <= endLine; i++)
            {
                bool hasStartMarker = this.lines.LineHasFoldingStartMarker(i);
                bool hasEndMarker = this.lines.LineHasFoldingEndMarker(i);

                if (hasEndMarker && hasStartMarker)
                {
                    continue;
                }

                if (hasStartMarker)
                {
                    stack.Push(i);
                }
                if (hasEndMarker)
                {
                    string m = this.lines[i].FoldingEndMarker;
                    while (stack.Count > 0)
                    {
                        int iStartLine = stack.Pop();
                        this.foldingPairs[iStartLine] = i;
                        if (m == this.lines[iStartLine].FoldingStartMarker)
                        {
                            break;
                        }
                    }
                }
            }

            while (stack.Count > 0)
            {
                this.foldingPairs[stack.Pop()] = endLine + 1;
            }
        }

        /// <summary>
        /// 	Collapse text block
        /// </summary>
        public virtual void CollapseBlock(int fromLine, int toLine)
        {
            int from = Math.Min(fromLine, toLine);
            int to = Math.Max(fromLine, toLine);
            if (from == to)
            {
                return;
            }

            //find first non empty line
            for (; from <= to; from++)
            {
                if (this.GetLineText(from).Trim().Length > 0)
                {
                    //hide lines
                    for (int i = from + 1; i <= to; i++)
                    {
                        this.SetVisibleState(i, VisibleState.Hidden);
                    }
                    this.SetVisibleState(from, VisibleState.StartOfHiddenBlock);
                    this.Invalidate();
                    break;
                }
            }
            //Move caret outside
            from = Math.Min(fromLine, toLine);
            to = Math.Max(fromLine, toLine);
            int newLine = this.FindNextVisibleLine(to);
            if (newLine == to)
            {
                newLine = this.FindPrevVisibleLine(from);
            }
            this.Selection.Start = new Place(0, newLine);
            //
            this.needRecalc = true;
            this.Invalidate();
            this.OnVisibleRangeChanged();
        }

        internal int FindNextVisibleLine(int iLine)
        {
            if (iLine >= this.lines.Count - 1)
            {
                return iLine;
            }
            int old = iLine;
            do iLine++;
            while (iLine < this.lines.Count - 1 && this.LineInfos[iLine].VisibleState != VisibleState.Visible);

            if (this.LineInfos[iLine].VisibleState != VisibleState.Visible)
            {
                return old;
            }
            else
            {
                return iLine;
            }
        }

        internal int FindPrevVisibleLine(int iLine)
        {
            if (iLine <= 0)
            {
                return iLine;
            }
            int old = iLine;
            do iLine--;
            while (iLine > 0 && this.LineInfos[iLine].VisibleState != VisibleState.Visible);

            if (this.LineInfos[iLine].VisibleState != VisibleState.Visible)
            {
                return old;
            }
            else
            {
                return iLine;
            }
        }

        private VisualMarker FindVisualMarkerForPoint(Point p)
        {
            foreach (var m in this.visibleMarkers)
            {
                if (m.rectangle.Contains(p))
                {
                    return m;
                }
            }
            return null;
        }

        /// <summary>
        /// 	Insert TAB into front of seletcted lines.
        /// </summary>
        public virtual void IncreaseIndent()
        {
            if (this.Selection.Start == this.Selection.End)
            {
                if (!this.Selection.ReadOnly)
                {
                    this.Selection.Start = new Place(
                        this[this.Selection.Start.iLine].StartSpacesCount, this.Selection.Start.iLine);
                    //insert tab as spaces
                    int spaces = this.TabLength - (this.Selection.Start.iChar % this.TabLength);
                    //replace mode? select forward chars
                    if (this.IsReplaceMode)
                    {
                        for (int i = 0; i < spaces; i++)
                        {
                            this.Selection.GoRight(true);
                        }
                        this.Selection.Inverse();
                    }

                    this.InsertText(new String(' ', spaces));
                }
                return;
            }

            bool carretAtEnd = (this.Selection.Start > this.Selection.End) && !this.Selection.ColumnSelectionMode;

            int startChar = 0; // Only move selection when in 'ColumnSelectionMode'
            if (this.Selection.ColumnSelectionMode)
            {
                startChar = Math.Min(this.Selection.End.iChar, this.Selection.Start.iChar);
            }

            this.BeginUpdate();
            this.Selection.BeginUpdate();
            this.lines.Manager.BeginAutoUndoCommands();

            Range old = this.Selection.Clone();
            this.lines.Manager.ExecuteCommand(new SelectCommand(this.TextSource)); //remember selection

            //
            this.Selection.Normalize();
            Range currentSelection = this.Selection.Clone();
            int from = this.Selection.Start.iLine;
            int to = this.Selection.End.iLine;

            if (!this.Selection.ColumnSelectionMode)
            {
                if (this.Selection.End.iChar == 0)
                {
                    to--;
                }
            }

            for (int i = from; i <= to; i++)
            {
                if (this.lines[i].Count == 0)
                {
                    continue;
                }
                this.Selection.Start = new Place(startChar, i);
                this.lines.Manager.ExecuteCommand(
                    new InsertTextCommand(this.TextSource, new String(' ', this.TabLength)));
            }

            // Restore selection
            if (this.Selection.ColumnSelectionMode == false)
            {
                int newSelectionStartCharacterIndex = currentSelection.Start.iChar + this.TabLength;
                int newSelectionEndCharacterIndex = currentSelection.End.iChar
                                                    + (currentSelection.End.iLine == to ? this.TabLength : 0);
                this.Selection.Start = new Place(newSelectionStartCharacterIndex, currentSelection.Start.iLine);
                this.Selection.End = new Place(newSelectionEndCharacterIndex, currentSelection.End.iLine);
            }
            else
            {
                this.Selection = old;
            }
            this.lines.Manager.EndAutoUndoCommands();

            if (carretAtEnd)
            {
                this.Selection.Inverse();
            }

            this.needRecalc = true;
            this.Selection.EndUpdate();
            this.EndUpdate();
            this.Invalidate();
        }

        /// <summary>
        /// 	Remove TAB from front of seletcted lines.
        /// </summary>
        public virtual void DecreaseIndent()
        {
            if (this.Selection.Start.iLine == this.Selection.End.iLine)
            {
                this.DecreaseIndentOfSingleLine();
                return;
            }

            int startCharIndex = 0;
            if (this.Selection.ColumnSelectionMode)
            {
                startCharIndex = Math.Min(this.Selection.End.iChar, this.Selection.Start.iChar);
            }

            this.BeginUpdate();
            this.Selection.BeginUpdate();
            this.lines.Manager.BeginAutoUndoCommands();
            Range old = this.Selection.Clone();
            this.lines.Manager.ExecuteCommand(new SelectCommand(this.TextSource)); //remember selection

            // Remember current selection infos
            Range currentSelection = this.Selection.Clone();
            this.Selection.Normalize();
            int from = this.Selection.Start.iLine;
            int to = this.Selection.End.iLine;

            if (!this.Selection.ColumnSelectionMode)
            {
                if (this.Selection.End.iChar == 0)
                {
                    to--;
                }
            }

            int numberOfDeletedWhitespacesOfFirstLine = 0;
            int numberOfDeletetWhitespacesOfLastLine = 0;

            for (int i = from; i <= to; i++)
            {
                if (startCharIndex > this.lines[i].Count)
                {
                    continue;
                }
                // Select first characters from the line
                int endIndex = Math.Min(this.lines[i].Count, startCharIndex + this.TabLength);
                string wasteText = this.lines[i].Text.Substring(startCharIndex, endIndex - startCharIndex);

                // Only select the first whitespace characters
                endIndex = Math.Min(endIndex, startCharIndex + wasteText.Length - wasteText.TrimStart().Length);

                // Select the characters to remove
                this.Selection = new Range(this, new Place(startCharIndex, i), new Place(endIndex, i));

                // Remember characters to remove for first and last line
                int numberOfWhitespacesToRemove = endIndex - startCharIndex;
                if (i == currentSelection.Start.iLine)
                {
                    numberOfDeletedWhitespacesOfFirstLine = numberOfWhitespacesToRemove;
                }
                if (i == currentSelection.End.iLine)
                {
                    numberOfDeletetWhitespacesOfLastLine = numberOfWhitespacesToRemove;
                }

                // Remove marked/selected whitespace characters
                if (!this.Selection.IsEmpty)
                {
                    this.ClearSelected();
                }
            }

            // Restore selection
            if (this.Selection.ColumnSelectionMode == false)
            {
                int newSelectionStartCharacterIndex = Math.Max(
                    0, currentSelection.Start.iChar - numberOfDeletedWhitespacesOfFirstLine);
                int newSelectionEndCharacterIndex = Math.Max(
                    0, currentSelection.End.iChar - numberOfDeletetWhitespacesOfLastLine);
                this.Selection.Start = new Place(newSelectionStartCharacterIndex, currentSelection.Start.iLine);
                this.Selection.End = new Place(newSelectionEndCharacterIndex, currentSelection.End.iLine);
            }
            else
            {
                this.Selection = old;
            }
            this.lines.Manager.EndAutoUndoCommands();

            this.needRecalc = true;
            this.Selection.EndUpdate();
            this.EndUpdate();
            this.Invalidate();
        }

        /// <summary>
        /// 	Remove TAB in front of the caret ot the selected line.
        /// </summary>
        protected virtual void DecreaseIndentOfSingleLine()
        {
            if (this.Selection.Start.iLine != this.Selection.End.iLine)
            {
                return;
            }

            // Remeber current selection infos
            Range currentSelection = this.Selection.Clone();
            int currentLineIndex = this.Selection.Start.iLine;
            int currentLeftSelectionStartIndex = Math.Min(this.Selection.Start.iChar, this.Selection.End.iChar);

            // Determine number of whitespaces to remove
            string lineText = this.lines[currentLineIndex].Text;
            Match whitespacesLeftOfSelectionStartMatch = new Regex(@"\s*", RegexOptions.RightToLeft).Match(
                lineText, currentLeftSelectionStartIndex);
            int leftOffset = whitespacesLeftOfSelectionStartMatch.Index;
            int countOfWhitespaces = whitespacesLeftOfSelectionStartMatch.Length;
            int numberOfCharactersToRemove = 0;
            if (countOfWhitespaces > 0)
            {
                int remainder = (this.TabLength > 0) ? currentLeftSelectionStartIndex % this.TabLength : 0;
                numberOfCharactersToRemove = (remainder != 0)
                                                 ? Math.Min(remainder, countOfWhitespaces)
                                                 : Math.Min(this.TabLength, countOfWhitespaces);
            }

            // Remove whitespaces if available
            if (numberOfCharactersToRemove > 0)
            {
                // Start selection update
                this.BeginUpdate();
                this.Selection.BeginUpdate();
                this.lines.Manager.BeginAutoUndoCommands();
                this.lines.Manager.ExecuteCommand(new SelectCommand(this.TextSource)); //remember selection

                // Remove whitespaces
                this.Selection.Start = new Place(leftOffset, currentLineIndex);
                this.Selection.End = new Place(leftOffset + numberOfCharactersToRemove, currentLineIndex);
                this.ClearSelected();

                // Restore selection
                int newSelectionStartCharacterIndex = currentSelection.Start.iChar - numberOfCharactersToRemove;
                int newSelectionEndCharacterIndex = currentSelection.End.iChar - numberOfCharactersToRemove;
                this.Selection.Start = new Place(newSelectionStartCharacterIndex, currentLineIndex);
                this.Selection.End = new Place(newSelectionEndCharacterIndex, currentLineIndex);

                this.lines.Manager.ExecuteCommand(new SelectCommand(this.TextSource)); //remember selection
                // End selection update
                this.lines.Manager.EndAutoUndoCommands();
                this.Selection.EndUpdate();
                this.EndUpdate();
            }

            this.Invalidate();
        }

        /// <summary>
        /// 	Insert autoindents into selected lines
        /// </summary>
        public virtual void DoAutoIndent()
        {
            if (this.Selection.ColumnSelectionMode)
            {
                return;
            }
            Range r = this.Selection.Clone();
            r.Normalize();
            //
            this.BeginUpdate();
            this.Selection.BeginUpdate();
            this.lines.Manager.BeginAutoUndoCommands();
            //
            for (int i = r.Start.iLine; i <= r.End.iLine; i++)
            {
                this.DoAutoIndent(i);
            }
            //
            this.lines.Manager.EndAutoUndoCommands();
            this.Selection.Start = r.Start;
            this.Selection.End = r.End;
            this.Selection.Expand();
            //
            this.Selection.EndUpdate();
            this.EndUpdate();
        }

        /// <summary>
        /// 	Insert prefix into front of seletcted lines
        /// </summary>
        public virtual void InsertLinePrefix(string prefix)
        {
            Range old = this.Selection.Clone();
            int from = Math.Min(this.Selection.Start.iLine, this.Selection.End.iLine);
            int to = Math.Max(this.Selection.Start.iLine, this.Selection.End.iLine);
            this.BeginUpdate();
            this.Selection.BeginUpdate();
            this.lines.Manager.BeginAutoUndoCommands();
            this.lines.Manager.ExecuteCommand(new SelectCommand(this.TextSource));
            int spaces = this.GetMinStartSpacesCount(from, to);
            for (int i = from; i <= to; i++)
            {
                this.Selection.Start = new Place(spaces, i);
                this.lines.Manager.ExecuteCommand(new InsertTextCommand(this.TextSource, prefix));
            }
            this.Selection.Start = new Place(0, from);
            this.Selection.End = new Place(this.lines[to].Count, to);
            this.needRecalc = true;
            this.lines.Manager.EndAutoUndoCommands();
            this.Selection.EndUpdate();
            this.EndUpdate();
            this.Invalidate();
        }

        /// <summary>
        /// 	Remove prefix from front of selected lines
        /// 	This method ignores forward spaces of the line
        /// </summary>
        public virtual void RemoveLinePrefix(string prefix)
        {
            Range old = this.Selection.Clone();
            int from = Math.Min(this.Selection.Start.iLine, this.Selection.End.iLine);
            int to = Math.Max(this.Selection.Start.iLine, this.Selection.End.iLine);
            this.BeginUpdate();
            this.Selection.BeginUpdate();
            this.lines.Manager.BeginAutoUndoCommands();
            this.lines.Manager.ExecuteCommand(new SelectCommand(this.TextSource));
            for (int i = from; i <= to; i++)
            {
                string text = this.lines[i].Text;
                string trimmedText = text.TrimStart();
                if (trimmedText.StartsWith(prefix))
                {
                    int spaces = text.Length - trimmedText.Length;
                    this.Selection.Start = new Place(spaces, i);
                    this.Selection.End = new Place(spaces + prefix.Length, i);
                    this.ClearSelected();
                }
            }
            this.Selection.Start = new Place(0, from);
            this.Selection.End = new Place(this.lines[to].Count, to);
            this.needRecalc = true;
            this.lines.Manager.EndAutoUndoCommands();
            this.Selection.EndUpdate();
            this.EndUpdate();
        }

        /// <summary>
        /// 	Begins AutoUndo block.
        /// 	All changes of text between BeginAutoUndo() and EndAutoUndo() will be canceled in one operation Undo.
        /// </summary>
        public void BeginAutoUndo()
        {
            this.lines.Manager.BeginAutoUndoCommands();
        }

        /// <summary>
        /// 	Ends AutoUndo block.
        /// 	All changes of text between BeginAutoUndo() and EndAutoUndo() will be canceled in one operation Undo.
        /// </summary>
        public void EndAutoUndo()
        {
            this.lines.Manager.EndAutoUndoCommands();
        }

        public virtual void OnVisualMarkerClick(MouseEventArgs args, StyleVisualMarker marker)
        {
            if (this.VisualMarkerClick != null)
            {
                this.VisualMarkerClick(this, new VisualMarkerEventArgs(marker.Style, marker, args));
            }
            marker.Style.OnVisualMarkerClick(this, new VisualMarkerEventArgs(marker.Style, marker, args));
        }

        protected virtual void OnMarkerClick(MouseEventArgs args, VisualMarker marker)
        {
            if (marker is StyleVisualMarker)
            {
                this.OnVisualMarkerClick(args, marker as StyleVisualMarker);
                return;
            }
            if (marker is CollapseFoldingMarker)
            {
                this.CollapseFoldingBlock((marker as CollapseFoldingMarker).iLine);
                return;
            }

            if (marker is ExpandFoldingMarker)
            {
                this.ExpandFoldedBlock((marker as ExpandFoldingMarker).iLine);
                return;
            }

            if (marker is FoldedAreaMarker)
            {
                //select folded block
                int iStart = (marker as FoldedAreaMarker).iLine;
                int iEnd = this.FindEndOfFoldingBlock(iStart);
                if (iEnd < 0)
                {
                    return;
                }
                this.Selection.BeginUpdate();
                this.Selection.Start = new Place(0, iStart);
                this.Selection.End = new Place(this.lines[iEnd].Count, iEnd);
                this.Selection.EndUpdate();
                this.Invalidate();
                return;
            }
        }

        protected virtual void OnMarkerDoubleClick(VisualMarker marker)
        {
            if (marker is FoldedAreaMarker)
            {
                this.ExpandFoldedBlock((marker as FoldedAreaMarker).iLine);
                this.Invalidate();
                return;
            }
        }

        private void ClearBracketsPositions()
        {
            this.leftBracketPosition = null;
            this.rightBracketPosition = null;
            this.leftBracketPosition2 = null;
            this.rightBracketPosition2 = null;
        }

        /// <summary>
        /// 	Highlights brackets around caret
        /// </summary>
        private void HighlightBrackets(
            char LeftBracket, char RightBracket, ref Range leftBracketPosition, ref Range rightBracketPosition)
        {
            switch (this.BracketsHighlightStrategy)
            {
                case BracketsHighlightStrategy.Strategy1:
                    this.HighlightBrackets1(
                        LeftBracket, RightBracket, ref leftBracketPosition, ref rightBracketPosition);
                    break;
                case BracketsHighlightStrategy.Strategy2:
                    this.HighlightBrackets2(
                        LeftBracket, RightBracket, ref leftBracketPosition, ref rightBracketPosition);
                    break;
            }
        }

        private void HighlightBrackets1(
            char LeftBracket, char RightBracket, ref Range leftBracketPosition, ref Range rightBracketPosition)
        {
            if (!this.Selection.IsEmpty)
            {
                return;
            }
            if (this.LinesCount == 0)
            {
                return;
            }
            //
            Range oldLeftBracketPosition = leftBracketPosition;
            Range oldRightBracketPosition = rightBracketPosition;
            Range range = this.Selection.Clone(); //need clone because we will move caret
            int counter = 0;
            int maxIterations = maxBracketSearchIterations;
            while (range.GoLeftThroughFolded()) //move caret left
            {
                if (range.CharAfterStart == LeftBracket)
                {
                    counter++;
                }
                if (range.CharAfterStart == RightBracket)
                {
                    counter--;
                }
                if (counter == 1)
                {
                    //highlighting
                    range.End = new Place(range.Start.iChar + 1, range.Start.iLine);
                    leftBracketPosition = range;
                    break;
                }
                //
                maxIterations--;
                if (maxIterations <= 0)
                {
                    break;
                }
            }
            //
            range = this.Selection.Clone(); //need clone because we will move caret
            counter = 0;
            maxIterations = maxBracketSearchIterations;
            do
            {
                if (range.CharAfterStart == LeftBracket)
                {
                    counter++;
                }
                if (range.CharAfterStart == RightBracket)
                {
                    counter--;
                }
                if (counter == -1)
                {
                    //highlighting
                    range.End = new Place(range.Start.iChar + 1, range.Start.iLine);
                    rightBracketPosition = range;
                    break;
                }
                //
                maxIterations--;
                if (maxIterations <= 0)
                {
                    break;
                }
            }
            while (range.GoRightThroughFolded()); //move caret right

            if (oldLeftBracketPosition != leftBracketPosition || oldRightBracketPosition != rightBracketPosition)
            {
                this.Invalidate();
            }
        }

        private void HighlightBrackets2(
            char LeftBracket, char RightBracket, ref Range leftBracketPosition, ref Range rightBracketPosition)
        {
            if (!this.Selection.IsEmpty)
            {
                return;
            }
            if (this.LinesCount == 0)
            {
                return;
            }
            //
            Range oldLeftBracketPosition = leftBracketPosition;
            Range oldRightBracketPosition = rightBracketPosition;
            Range range = this.Selection.Clone(); //need clone because we will move caret

            bool found = false;
            int counter = 0;
            int maxIterations = maxBracketSearchIterations;
            if (range.CharBeforeStart == RightBracket)
            {
                rightBracketPosition = new Range(
                    this, range.Start.iChar - 1, range.Start.iLine, range.Start.iChar, range.Start.iLine);
                while (range.GoLeftThroughFolded()) //move caret left
                {
                    if (range.CharAfterStart == LeftBracket)
                    {
                        counter++;
                    }
                    if (range.CharAfterStart == RightBracket)
                    {
                        counter--;
                    }
                    if (counter == 0)
                    {
                        //highlighting
                        range.End = new Place(range.Start.iChar + 1, range.Start.iLine);
                        leftBracketPosition = range;
                        found = true;
                        break;
                    }
                    //
                    maxIterations--;
                    if (maxIterations <= 0)
                    {
                        break;
                    }
                }
            }
            //
            range = this.Selection.Clone(); //need clone because we will move caret
            counter = 0;
            maxIterations = maxBracketSearchIterations;
            if (!found)
            {
                if (range.CharAfterStart == LeftBracket)
                {
                    leftBracketPosition = new Range(
                        this, range.Start.iChar, range.Start.iLine, range.Start.iChar + 1, range.Start.iLine);
                    do
                    {
                        if (range.CharAfterStart == LeftBracket)
                        {
                            counter++;
                        }
                        if (range.CharAfterStart == RightBracket)
                        {
                            counter--;
                        }
                        if (counter == 0)
                        {
                            //highlighting
                            range.End = new Place(range.Start.iChar + 1, range.Start.iLine);
                            rightBracketPosition = range;
                            found = true;
                            break;
                        }
                        //
                        maxIterations--;
                        if (maxIterations <= 0)
                        {
                            break;
                        }
                    }
                    while (range.GoRightThroughFolded()); //move caret right
                }
            }

            if (oldLeftBracketPosition != leftBracketPosition || oldRightBracketPosition != rightBracketPosition)
            {
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Selectes next fragment for given regex.
        /// </summary>
        public bool SelectNext(string regexPattern, bool backward = false, RegexOptions options = RegexOptions.None)
        {
            Range sel = this.Selection.Clone();
            sel.Normalize();
            Range range1 = backward
                               ? new Range(this, this.Range.Start, sel.Start)
                               : new Range(this, sel.End, this.Range.End);

            Range res = null;
            foreach (var r in range1.GetRanges(regexPattern, options))
            {
                res = r;
                if (!backward)
                {
                    break;
                }
            }

            if (res == null)
            {
                return false;
            }
            this.Selection = res;
            this.Invalidate();
            return true;
        }

        public virtual void OnSyntaxHighlight(TextChangedEventArgs args)
        {
#if debug
            Stopwatch sw = Stopwatch.StartNew();
            #endif

            Range range;

            switch (this.HighlightingRangeType)
            {
                case HighlightingRangeType.VisibleRange:
                    range = this.VisibleRange.GetUnionWith(args.ChangedRange);
                    break;
                case HighlightingRangeType.AllTextRange:
                    range = this.Range;
                    break;
                default:
                    range = args.ChangedRange;
                    break;
            }

            if (this.SyntaxHighlighter != null)
            {
                if (this.Language == Language.Custom && !string.IsNullOrEmpty(this.DescriptionFile))
                {
                    this.SyntaxHighlighter.HighlightSyntax(this.DescriptionFile, range);
                }
                else
                {
                    this.SyntaxHighlighter.HighlightSyntax(this.Language, range);
                }
            }

#if debug
            Console.WriteLine("OnSyntaxHighlight: "+ sw.ElapsedMilliseconds);
#endif
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // FastColoredTextBox
            // 
            this.Name = "FastColoredTextBox";
            this.ResumeLayout(false);
        }

        /// <summary>
        /// 	Prints range of text
        /// </summary>
        public virtual void Print(Range range, PrintDialogSettings settings)
        {
            //prepare export with wordwrapping
            var exporter = new ExportToHTML();
            exporter.UseBr = true;
            exporter.UseForwardNbsp = true;
            exporter.UseNbsp = true;
            exporter.UseStyleTag = false;
            exporter.IncludeLineNumbers = settings.IncludeLineNumbers;

            if (range == null)
            {
                range = this.Range;
            }

            if (range.Text == string.Empty)
            {
                return;
            }

            //change visible range
            this.visibleRange = range;
            try
            {
                //call handlers for VisibleRange
                if (this.VisibleRangeChanged != null)
                {
                    this.VisibleRangeChanged(this, new EventArgs());
                }
                if (this.VisibleRangeChangedDelayed != null)
                {
                    this.VisibleRangeChangedDelayed(this, new EventArgs());
                }
            }
            finally
            {
                //restore visible range
                this.visibleRange = null;
            }

            //generate HTML
            string HTML = exporter.GetHtml(range);
            HTML = "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\"><head><title>"
                   + this.PrepareHtmlText(settings.Title) + "</title></head>" + HTML + "<br>"
                   + this.SelectHTMLRangeScript();
            string tempFile = Path.GetTempPath() + "fctb.html";
            File.WriteAllText(tempFile, HTML);

            //clear wb page setup settings
            SetPageSetupSettings(settings);

            //create wb
            var wb = new WebBrowser();
            wb.Tag = settings;
            wb.Visible = false;
            wb.Location = new Point(-1000, -1000);
            wb.Parent = this;
            wb.StatusTextChanged += this.wb_StatusTextChanged;
            wb.Navigate(tempFile);
        }

        protected virtual string PrepareHtmlText(string s)
        {
            return s.Replace("<", "&lt;").Replace(">", "&gt;").Replace("&", "&amp;");
        }

        private void wb_StatusTextChanged(object sender, EventArgs e)
        {
            var wb = sender as WebBrowser;
            if (wb.StatusText.Contains("#print"))
            {
                var settings = wb.Tag as PrintDialogSettings;
                try
                {
                    //show print dialog
                    if (settings.ShowPrintPreviewDialog)
                    {
                        wb.ShowPrintPreviewDialog();
                    }
                    else
                    {
                        if (settings.ShowPageSetupDialog)
                        {
                            wb.ShowPageSetupDialog();
                        }

                        if (settings.ShowPrintDialog)
                        {
                            wb.ShowPrintDialog();
                        }
                        else
                        {
                            wb.Print();
                        }
                    }
                }
                finally
                {
                    //destroy webbrowser
                    wb.Parent = null;
                    wb.Dispose();
                }
            }
        }

        /// <summary>
        /// 	Prints all text
        /// </summary>
        public void Print(PrintDialogSettings settings)
        {
            this.Print(this.Range, settings);
        }

        /// <summary>
        /// 	Prints all text, without any dialog windows
        /// </summary>
        public void Print()
        {
            this.Print(
                this.Range,
                new PrintDialogSettings
                    { ShowPageSetupDialog = false, ShowPrintDialog = false, ShowPrintPreviewDialog = false });
        }

        private string SelectHTMLRangeScript()
        {
            Range sel = this.Selection.Clone();
            sel.Normalize();
            int start = this.PlaceToPosition(sel.Start) - sel.Start.iLine;
            int len = sel.Text.Length - (sel.End.iLine - sel.Start.iLine);
            return string.Format(@"<script type=""text/javascript"">
try{{
    var sel = document.selection;
    var rng = sel.createRange();
    rng.moveStart(""character"", {0});
    rng.moveEnd(""character"", {1});
    rng.select();
}}catch(ex){{}}
window.status = ""#print"";
</script>", start, len);
        }

        private static void SetPageSetupSettings(PrintDialogSettings settings)
        {
            RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Internet Explorer\PageSetup", true);
            if (key != null)
            {
                key.SetValue("footer", settings.Footer);
                key.SetValue("header", settings.Header);
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                if (this.SyntaxHighlighter != null)
                {
                    this.SyntaxHighlighter.Dispose();
                }
                this.timer.Dispose();
                this.timer2.Dispose();
                this.middleClickScrollingTimer.Dispose();

                if (this.findForm != null)
                {
                    this.findForm.Dispose();
                }

                if (this.replaceForm != null)
                {
                    this.replaceForm.Dispose();
                }
                /*
                if (Font != null)
                    Font.Dispose();

                if (originalFont != null)
                    originalFont.Dispose();*/

                if (this.TextSource != null)
                {
                    this.TextSource.Dispose();
                }

                if (this.ToolTip != null)
                {
                    this.ToolTip.Dispose();
                }
            }
        }

        protected virtual void OnPaintLine(PaintLineEventArgs e)
        {
            if (this.PaintLine != null)
            {
                this.PaintLine(this, e);
            }
        }

        internal void OnLineInserted(int index)
        {
            this.OnLineInserted(index, 1);
        }

        internal void OnLineInserted(int index, int count)
        {
            if (this.LineInserted != null)
            {
                this.LineInserted(this, new LineInsertedEventArgs(index, count));
            }
        }

        internal void OnLineRemoved(int index, int count, List<int> removedLineIds)
        {
            if (count > 0)
            {
                if (this.LineRemoved != null)
                {
                    this.LineRemoved(this, new LineRemovedEventArgs(index, count, removedLineIds));
                }
            }
        }

        /// <summary>
        /// 	Open text file
        /// </summary>
        public void OpenFile(string fileName, Encoding enc)
        {
            TextSource ts = this.CreateTextSource();
            try
            {
                this.InitTextSource(ts);
                this.Text = File.ReadAllText(fileName, enc);
                this.ClearUndo();
                this.IsChanged = false;
                this.OnVisibleRangeChanged();
            }
            catch
            {
                this.InitTextSource(this.CreateTextSource());
                this.lines.InsertLine(0, this.TextSource.CreateLine());
                this.IsChanged = false;
                throw;
            }
            this.Selection.Start = Place.Empty;
            this.DoSelectionVisible();
        }

        /// <summary>
        /// 	Open text file (with automatic encoding detector)
        /// </summary>
        public void OpenFile(string fileName)
        {
            try
            {
                Encoding enc = EncodingDetector.DetectTextFileEncoding(fileName);
                if (enc != null)
                {
                    this.OpenFile(fileName, enc);
                }
                else
                {
                    this.OpenFile(fileName, Encoding.Default);
                }
            }
            catch
            {
                this.InitTextSource(this.CreateTextSource());
                this.lines.InsertLine(0, this.TextSource.CreateLine());
                this.IsChanged = false;
                throw;
            }
        }

        /// <summary>
        /// 	Open file binding mode
        /// </summary>
        /// <param name="fileName"> </param>
        /// <param name="enc"> </param>
        public void OpenBindingFile(string fileName, Encoding enc)
        {
            var fts = new FileTextSource(this);
            try
            {
                this.InitTextSource(fts);
                fts.OpenFile(fileName, enc);
                this.IsChanged = false;
                this.OnVisibleRangeChanged();
            }
            catch
            {
                fts.CloseFile();
                this.InitTextSource(this.CreateTextSource());
                this.lines.InsertLine(0, this.TextSource.CreateLine());
                this.IsChanged = false;
                throw;
            }
            this.Invalidate();
        }

        /// <summary>
        /// 	Close file binding mode
        /// </summary>
        public void CloseBindingFile()
        {
            if (this.lines is FileTextSource)
            {
                var fts = this.lines as FileTextSource;
                fts.CloseFile();

                this.InitTextSource(this.CreateTextSource());
                this.lines.InsertLine(0, this.TextSource.CreateLine());
                this.IsChanged = false;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Save text to the file
        /// </summary>
        /// <param name="fileName"> </param>
        /// <param name="enc"> </param>
        public void SaveToFile(string fileName, Encoding enc)
        {
            this.lines.SaveToFile(fileName, enc);
            this.IsChanged = false;
            this.OnVisibleRangeChanged();
            this.UpdateScrollbars();
        }

        /// <summary>
        /// 	Set VisibleState of line
        /// </summary>
        public void SetVisibleState(int iLine, VisibleState state)
        {
            LineInfo li = this.LineInfos[iLine];
            li.VisibleState = state;
            this.LineInfos[iLine] = li;
            this.needRecalc = true;
        }

        /// <summary>
        /// 	Returns VisibleState of the line
        /// </summary>
        public VisibleState GetVisibleState(int iLine)
        {
            return this.LineInfos[iLine].VisibleState;
        }

        /// <summary>
        /// 	Shows Goto dialog form
        /// </summary>
        public void ShowGoToDialog()
        {
            var form = new GoToForm();
            form.TotalLineCount = this.LinesCount;
            form.SelectedLineNumber = this.Selection.Start.iLine + 1;

            if (form.ShowDialog() == DialogResult.OK)
            {
                int line = Math.Min(this.LinesCount - 1, Math.Max(0, form.SelectedLineNumber - 1));
                this.Selection = new Range(this, 0, line, 0, line);
                this.DoSelectionVisible();
            }
        }

        /// <summary>
        /// 	Occurs when undo/redo stack is changed
        /// </summary>
        public void OnUndoRedoStateChanged()
        {
            if (this.UndoRedoStateChanged != null)
            {
                this.UndoRedoStateChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 	Search lines by regex pattern
        /// </summary>
        public List<int> FindLines(string searchPattern, RegexOptions options)
        {
            var iLines = new List<int>();
            foreach (var r in this.Range.GetRangesByLines(searchPattern, options))
            {
                iLines.Add(r.Start.iLine);
            }

            return iLines;
        }

        /// <summary>
        /// 	Removes given lines
        /// </summary>
        public void RemoveLines(List<int> iLines)
        {
            this.TextSource.Manager.ExecuteCommand(new RemoveLinesCommand(this.TextSource, iLines));
            if (iLines.Count > 0)
            {
                this.IsChanged = true;
            }
            if (this.LinesCount == 0)
            {
                this.Text = "";
            }
            this.NeedRecalc();
            this.Invalidate();
        }

        void ISupportInitialize.BeginInit()
        {
            //
        }

        void ISupportInitialize.EndInit()
        {
            this.OnTextChanged();
            this.Selection.Start = Place.Empty;
            this.DoCaretVisible();
            this.IsChanged = false;
            this.ClearUndo();
        }

        #region Drag and drop

        private bool IsDragDrop { get; set; }

        protected override void OnDragEnter(DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.Text) && this.AllowDrop)
            {
                e.Effect = DragDropEffects.Copy;
                this.IsDragDrop = true;
            }
            base.OnDragEnter(e);
        }

        protected override void OnDragDrop(DragEventArgs e)
        {
            if (this.ReadOnly || !this.AllowDrop)
            {
                this.IsDragDrop = false;
                return;
            }

            if (e.Data.GetDataPresent(DataFormats.Text))
            {
                if (this.ParentForm != null)
                {
                    this.ParentForm.Activate();
                }
                this.Focus();
                Point p = this.PointToClient(new Point(e.X, e.Y));
                string text = e.Data.GetData(DataFormats.Text).ToString();
                Place place = this.PointToPlace(p);
                DoDragDrop(place, text);
                this.IsDragDrop = false;
            }
            base.OnDragDrop(e);
        }

        private void DoDragDrop_old(Place place, string text)
        {
            var insertRange = new Range(this, place, place);

            // Abort, if insertRange is read only
            if (insertRange.ReadOnly)
            {
                return;
            }

            // Abort, if dragged range contains target place
            if ((this.draggedRange != null) && this.draggedRange.Contains(place))
            {
                return;
            }

            // Determine, if the dragged string should be copied or moved
            bool copyMode = (this.draggedRange == null) || // drag from outside
                            (this.draggedRange.ReadOnly) || // dragged range is read only
                            ((ModifierKeys & Keys.Control) != Keys.None);

            //drag from outside?
            if (this.draggedRange == null)
            {
                this.Selection.BeginUpdate();
                // Insert text
                this.Selection.Start = place;
                this.InsertText(text);
                // Select inserted text
                this.Selection = new Range(this, place, this.Selection.Start);
                this.Selection.EndUpdate();
                return;
            }

            //drag from me
            Place caretPositionAfterInserting;
            this.BeginAutoUndo();
            this.Selection.BeginUpdate();

            //remember dragged selection for undo/redo
            this.Selection = this.draggedRange;
            this.lines.Manager.ExecuteCommand(new SelectCommand(this.lines));
            //
            if (this.draggedRange.ColumnSelectionMode)
            {
                this.draggedRange.Normalize();
                insertRange = new Range(
                    this,
                    place,
                    new Place(place.iChar, place.iLine + this.draggedRange.End.iLine - this.draggedRange.Start.iLine))
                    { ColumnSelectionMode = true };
                for (int i = this.LinesCount; i <= insertRange.End.iLine; i++)
                {
                    this.Selection.GoLast(false);
                    this.InsertChar('\n');
                }
            }

            if (!insertRange.ReadOnly)
            {
                if (place < this.draggedRange.Start)
                {
                    // Delete dragged range if not in copy mode
                    if (copyMode == false)
                    {
                        this.Selection = this.draggedRange;
                        this.ClearSelected();
                    }

                    // Insert text
                    this.Selection = insertRange;
                    this.Selection.ColumnSelectionMode = insertRange.ColumnSelectionMode;
                    this.InsertText(text);
                    caretPositionAfterInserting = this.Selection.Start;
                }
                else
                {
                    // Insert text
                    this.Selection = insertRange;
                    this.Selection.ColumnSelectionMode = insertRange.ColumnSelectionMode;
                    this.InsertText(text);
                    caretPositionAfterInserting = this.Selection.Start;
                    int lineLength = this[caretPositionAfterInserting.iLine].Count;

                    // Delete dragged range if not in copy mode
                    if (copyMode == false)
                    {
                        this.Selection = this.draggedRange;
                        this.ClearSelected();
                    }

                    int shift = lineLength - this[caretPositionAfterInserting.iLine].Count;
                    caretPositionAfterInserting.iChar = caretPositionAfterInserting.iChar - shift;
                    place.iChar = place.iChar - shift;
                }

                // Select inserted text
                if (!this.draggedRange.ColumnSelectionMode)
                {
                    this.Selection = new Range(this, place, caretPositionAfterInserting);
                }
                else
                {
                    this.draggedRange.Normalize();
                    this.Selection = new Range(
                        this,
                        place,
                        new Place(
                            place.iChar + this.draggedRange.End.iChar - this.draggedRange.Start.iChar,
                            place.iLine + this.draggedRange.End.iLine - this.draggedRange.Start.iLine))
                        { ColumnSelectionMode = true };
                }
            }

            this.Selection.EndUpdate();
            this.EndAutoUndo();
            this.draggedRange = null;
        }

        protected virtual void DoDragDrop(Place place, string text)
        {
            var insertRange = new Range(this, place, place);

            // Abort, if insertRange is read only
            if (insertRange.ReadOnly)
            {
                return;
            }

            // Abort, if dragged range contains target place
            if ((this.draggedRange != null) && this.draggedRange.Contains(place))
            {
                return;
            }

            // Determine, if the dragged string should be copied or moved
            bool copyMode = (this.draggedRange == null) || // drag from outside
                            (this.draggedRange.ReadOnly) || // dragged range is read only
                            ((ModifierKeys & Keys.Control) != Keys.None);

            if (this.draggedRange == null) //drag from outside
            {
                this.Selection.BeginUpdate();
                // Insert text
                this.Selection.Start = place;
                this.InsertText(text);
                // Select inserted text
                this.Selection = new Range(this, place, this.Selection.Start);
                this.Selection.EndUpdate();
            }
            else //drag from me
            {
                if (!this.draggedRange.Contains(place))
                {
                    this.BeginAutoUndo();
                    this.Selection.BeginUpdate();

                    //remember dragged selection for undo/redo
                    this.Selection = this.draggedRange;
                    this.lines.Manager.ExecuteCommand(new SelectCommand(this.lines));
                    //
                    if (this.draggedRange.ColumnSelectionMode)
                    {
                        this.draggedRange.Normalize();
                        insertRange = new Range(
                            this,
                            place,
                            new Place(
                                place.iChar, place.iLine + this.draggedRange.End.iLine - this.draggedRange.Start.iLine))
                            { ColumnSelectionMode = true };
                        for (int i = this.LinesCount; i <= insertRange.End.iLine; i++)
                        {
                            this.Selection.GoLast(false);
                            this.InsertChar('\n');
                        }
                    }

                    if (!insertRange.ReadOnly)
                    {
                        if (place < this.draggedRange.Start)
                        {
                            // Delete dragged range if not in copy mode
                            if (copyMode == false)
                            {
                                this.Selection = this.draggedRange;
                                this.ClearSelected();
                            }

                            // Insert text
                            this.Selection = insertRange;
                            this.Selection.ColumnSelectionMode = insertRange.ColumnSelectionMode;
                            this.InsertText(text);
                        }
                        else
                        {
                            // Insert text
                            this.Selection = insertRange;
                            this.Selection.ColumnSelectionMode = insertRange.ColumnSelectionMode;
                            this.InsertText(text);

                            // Delete dragged range if not in copy mode
                            if (copyMode == false)
                            {
                                this.Selection = this.draggedRange;
                                this.ClearSelected();
                            }
                        }
                    }

                    // Selection start and end position
                    Place startPosition = place;
                    Place endPosition = this.Selection.Start;

                    // Correct selection
                    Range dR = (this.draggedRange.End > this.draggedRange.Start) // dragged selection
                                   ? this.GetRange(this.draggedRange.Start, this.draggedRange.End)
                                   : this.GetRange(this.draggedRange.End, this.draggedRange.Start);
                    Place tP = place; // targetPlace
                    int tS_S_Line; // targetSelection.Start.iLine
                    int tS_S_Char; // targetSelection.Start.iChar
                    int tS_E_Line; // targetSelection.End.iLine
                    int tS_E_Char; // targetSelection.End.iChar
                    if ((place > this.draggedRange.Start) && (copyMode == false))
                    {
                        if (this.draggedRange.ColumnSelectionMode == false)
                        {
                            // Normal selection mode:

                            // Determine character/column position of target selection
                            if (dR.Start.iLine != dR.End.iLine) // If more then one line was selected/dragged ...
                            {
                                tS_S_Char = (dR.End.iLine != tP.iLine)
                                                ? tP.iChar
                                                : dR.Start.iChar + (tP.iChar - dR.End.iChar);
                                tS_E_Char = dR.End.iChar;
                            }
                            else // only one line was selected/dragged
                            {
                                if (dR.End.iLine == tP.iLine)
                                {
                                    tS_S_Char = tP.iChar - dR.Text.Length;
                                    tS_E_Char = tP.iChar;
                                }
                                else
                                {
                                    tS_S_Char = tP.iChar;
                                    tS_E_Char = tP.iChar + dR.Text.Length;
                                }
                            }

                            // Determine line/row of target selection
                            if (dR.End.iLine != tP.iLine)
                            {
                                tS_S_Line = tP.iLine - (dR.End.iLine - dR.Start.iLine);
                                tS_E_Line = tP.iLine;
                            }
                            else
                            {
                                tS_S_Line = dR.Start.iLine;
                                tS_E_Line = dR.End.iLine;
                            }

                            startPosition = new Place(tS_S_Char, tS_S_Line);
                            endPosition = new Place(tS_E_Char, tS_E_Line);
                        }
                    }

                    // Select inserted text
                    if (!this.draggedRange.ColumnSelectionMode)
                    {
                        this.Selection = new Range(this, startPosition, endPosition);
                    }
                    else
                    {
                        if ((copyMode == false) && (place.iLine >= dR.Start.iLine) && (place.iLine <= dR.End.iLine)
                            && (place.iChar >= dR.End.iChar))
                        {
                            tS_S_Char = tP.iChar - (dR.End.iChar - dR.Start.iChar);
                            tS_E_Char = tP.iChar;
                        }
                        else
                        {
                            tS_S_Char = tP.iChar;
                            tS_E_Char = tP.iChar + (dR.End.iChar - dR.Start.iChar);
                        }
                        tS_S_Line = tP.iLine;
                        tS_E_Line = tP.iLine + (dR.End.iLine - dR.Start.iLine);

                        startPosition = new Place(tS_S_Char, tS_S_Line);
                        endPosition = new Place(tS_E_Char, tS_E_Line);
                        this.Selection = new Range(this, startPosition, endPosition) { ColumnSelectionMode = true };
                    }

                    this.Range.EndUpdate();
                    this.EndAutoUndo();
                }
                this.selection.Inverse();
            }
            this.draggedRange = null;
        }

        protected override void OnDragOver(DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.Text))
            {
                Point p = this.PointToClient(new Point(e.X, e.Y));
                this.Selection.Start = this.PointToPlace(p);
                if (p.Y < 6 && this.VerticalScroll.Visible && this.VerticalScroll.Value > 0)
                {
                    this.VerticalScroll.Value = Math.Max(0, this.VerticalScroll.Value - this.charHeight);
                }

                this.DoCaretVisible();
                this.Invalidate();
            }
            base.OnDragOver(e);
        }

        protected override void OnDragLeave(EventArgs e)
        {
            this.IsDragDrop = false;
            base.OnDragLeave(e);
        }

        #endregion

        #region MiddleClickScrolling

        private bool middleClickScrollingActivated;

        private Point middleClickScrollingOriginPoint;

        private Point middleClickScrollingOriginScroll;

        private readonly Timer middleClickScrollingTimer = new Timer();

        private ScrollDirection middleClickScollDirection = ScrollDirection.None;

        /// <summary>
        /// 	Activates the scrolling mode (middle click button).
        /// </summary>
        /// <param name="e"> MouseEventArgs </param>
        private void ActivateMiddleClickScrollingMode(MouseEventArgs e)
        {
            if (!this.middleClickScrollingActivated)
            {
                if ((!this.HorizontalScroll.Visible) && (!this.VerticalScroll.Visible))
                {
                    if (this.ShowScrollBars)
                    {
                        return;
                    }
                }
                this.middleClickScrollingActivated = true;
                this.middleClickScrollingOriginPoint = e.Location;
                this.middleClickScrollingOriginScroll = new Point(
                    this.HorizontalScroll.Value, this.VerticalScroll.Value);
                this.middleClickScrollingTimer.Interval = 50;
                this.middleClickScrollingTimer.Enabled = true;
                this.Capture = true;
                // Refresh the control 
                this.Refresh();
                // Disable drawing
                SendMessage(this.Handle, WM_SETREDRAW, 0, 0);
            }
        }

        /// <summary>
        /// 	Deactivates the scrolling mode (middle click button).
        /// </summary>
        private void DeactivateMiddleClickScrollingMode()
        {
            if (this.middleClickScrollingActivated)
            {
                this.middleClickScrollingActivated = false;
                this.middleClickScrollingTimer.Enabled = false;
                this.Capture = false;
                base.Cursor = this.defaultCursor;
                // Enable drawing
                SendMessage(this.Handle, WM_SETREDRAW, 1, 0);
                this.Invalidate();
            }
        }

        /// <summary>
        /// 	Restore scrolls
        /// </summary>
        private void RestoreScrollsAfterMiddleClickScrollingMode()
        {
            var xea = new ScrollEventArgs(
                ScrollEventType.ThumbPosition,
                this.HorizontalScroll.Value,
                this.middleClickScrollingOriginScroll.X,
                ScrollOrientation.HorizontalScroll);
            this.OnScroll(xea);

            var yea = new ScrollEventArgs(
                ScrollEventType.ThumbPosition,
                this.VerticalScroll.Value,
                this.middleClickScrollingOriginScroll.Y,
                ScrollOrientation.VerticalScroll);
            this.OnScroll(yea);
        }

        [DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

        private const int WM_SETREDRAW = 0xB;

        private void middleClickScrollingTimer_Tick(object sender, EventArgs e)
        {
            if (this.IsDisposed)
            {
                return;
            }

            if (!this.middleClickScrollingActivated)
            {
                return;
            }

            Point currentMouseLocation = this.PointToClient(Cursor.Position);

            this.Capture = true;

            // Calculate angle and distance between current position point and origin point
            int distanceX = this.middleClickScrollingOriginPoint.X - currentMouseLocation.X;
            int distanceY = this.middleClickScrollingOriginPoint.Y - currentMouseLocation.Y;

            if (!this.VerticalScroll.Visible && this.ShowScrollBars)
            {
                distanceY = 0;
            }
            if (!this.HorizontalScroll.Visible && this.ShowScrollBars)
            {
                distanceX = 0;
            }

            double angleInDegree = 180 - Math.Atan2(distanceY, distanceX) * 180 / Math.PI;
            double distance = Math.Sqrt(Math.Pow(distanceX, 2) + Math.Pow(distanceY, 2));

            // determine scrolling direction depending on the angle
            if (distance > 10)
            {
                if (angleInDegree >= 325 || angleInDegree <= 35)
                {
                    this.middleClickScollDirection = ScrollDirection.Right;
                }
                else if (angleInDegree <= 55)
                {
                    this.middleClickScollDirection = ScrollDirection.Right | ScrollDirection.Up;
                }
                else if (angleInDegree <= 125)
                {
                    this.middleClickScollDirection = ScrollDirection.Up;
                }
                else if (angleInDegree <= 145)
                {
                    this.middleClickScollDirection = ScrollDirection.Up | ScrollDirection.Left;
                }
                else if (angleInDegree <= 215)
                {
                    this.middleClickScollDirection = ScrollDirection.Left;
                }
                else if (angleInDegree <= 235)
                {
                    this.middleClickScollDirection = ScrollDirection.Left | ScrollDirection.Down;
                }
                else if (angleInDegree <= 305)
                {
                    this.middleClickScollDirection = ScrollDirection.Down;
                }
                else
                {
                    this.middleClickScollDirection = ScrollDirection.Down | ScrollDirection.Right;
                }
            }
            else
            {
                this.middleClickScollDirection = ScrollDirection.None;
            }

            // Set mouse cursor
            switch (this.middleClickScollDirection)
            {
                case ScrollDirection.Right:
                    base.Cursor = Cursors.PanEast;
                    break;
                case ScrollDirection.Right | ScrollDirection.Up:
                    base.Cursor = Cursors.PanNE;
                    break;
                case ScrollDirection.Up:
                    base.Cursor = Cursors.PanNorth;
                    break;
                case ScrollDirection.Up | ScrollDirection.Left:
                    base.Cursor = Cursors.PanNW;
                    break;
                case ScrollDirection.Left:
                    base.Cursor = Cursors.PanWest;
                    break;
                case ScrollDirection.Left | ScrollDirection.Down:
                    base.Cursor = Cursors.PanSW;
                    break;
                case ScrollDirection.Down:
                    base.Cursor = Cursors.PanSouth;
                    break;
                case ScrollDirection.Down | ScrollDirection.Right:
                    base.Cursor = Cursors.PanSE;
                    break;
                default:
                    base.Cursor = this.defaultCursor;
                    return;
            }

            var xScrollOffset = (int)(-distanceX / 5.0);
            var yScrollOffset = (int)(-distanceY / 5.0);

            var xea =
                new ScrollEventArgs(
                    xScrollOffset < 0 ? ScrollEventType.SmallIncrement : ScrollEventType.SmallDecrement,
                    this.HorizontalScroll.Value,
                    this.HorizontalScroll.Value + xScrollOffset,
                    ScrollOrientation.HorizontalScroll);

            var yea =
                new ScrollEventArgs(
                    yScrollOffset < 0 ? ScrollEventType.SmallDecrement : ScrollEventType.SmallIncrement,
                    this.VerticalScroll.Value,
                    this.VerticalScroll.Value + yScrollOffset,
                    ScrollOrientation.VerticalScroll);

            if ((this.middleClickScollDirection & (ScrollDirection.Down | ScrollDirection.Up)) > 0)
            {
                //DoScrollVertical(1 + Math.Abs(yScrollOffset), Math.Sign(distanceY));
                this.OnScroll(yea, false);
            }

            if ((this.middleClickScollDirection & (ScrollDirection.Right | ScrollDirection.Left)) > 0)
            {
                this.OnScroll(xea);
            }

            // Enable drawing
            SendMessage(this.Handle, WM_SETREDRAW, 1, 0);
            // Refresh the control 
            this.Refresh();
            // Disable drawing
            SendMessage(this.Handle, WM_SETREDRAW, 0, 0);
        }

        private void DrawMiddleClickScrolling(Graphics gr)
        {
            // If mouse scrolling mode activated draw the scrolling cursor image
            bool ableToScrollVertically = this.VerticalScroll.Visible || !this.ShowScrollBars;
            bool ableToScrollHorizontally = this.HorizontalScroll.Visible || !this.ShowScrollBars;

            // Calculate inverse color
            Color inverseColor = Color.FromArgb(
                100, (byte)~this.BackColor.R, (byte)~this.BackColor.G, (byte)~this.BackColor.B);
            using (var inverseColorBrush = new SolidBrush(inverseColor))
            {
                Point p = this.middleClickScrollingOriginPoint;

                GraphicsState state = gr.Save();

                gr.SmoothingMode = SmoothingMode.HighQuality;
                gr.TranslateTransform(p.X, p.Y);
                gr.FillEllipse(inverseColorBrush, -2, -2, 4, 4);

                if (ableToScrollVertically)
                {
                    this.DrawTriangle(gr, inverseColorBrush);
                }
                gr.RotateTransform(90);
                if (ableToScrollHorizontally)
                {
                    this.DrawTriangle(gr, inverseColorBrush);
                }
                gr.RotateTransform(90);
                if (ableToScrollVertically)
                {
                    this.DrawTriangle(gr, inverseColorBrush);
                }
                gr.RotateTransform(90);
                if (ableToScrollHorizontally)
                {
                    this.DrawTriangle(gr, inverseColorBrush);
                }

                gr.Restore(state);
            }
        }

        private void DrawTriangle(Graphics g, Brush brush)
        {
            const int size = 5;
            var points = new[] { new Point(size, 2 * size), new Point(0, 3 * size), new Point(-size, 2 * size) };
            g.FillPolygon(brush, points);
        }

        #endregion

        #region Nested type: LineYComparer

        private class LineYComparer : IComparer<LineInfo>
        {
            private readonly int Y;

            public LineYComparer(int Y)
            {
                this.Y = Y;
            }

            #region IComparer<LineInfo> Members

            public int Compare(LineInfo x, LineInfo y)
            {
                if (x.startY == -10)
                {
                    return -y.startY.CompareTo(this.Y);
                }
                else
                {
                    return x.startY.CompareTo(this.Y);
                }
            }

            #endregion
        }

        #endregion
    }

    public class PaintLineEventArgs : PaintEventArgs
    {
        public PaintLineEventArgs(int iLine, Rectangle rect, Graphics gr, Rectangle clipRect)
            : base(gr, clipRect)
        {
            this.LineIndex = iLine;
            this.LineRect = rect;
        }

        public int LineIndex { get; private set; }

        public Rectangle LineRect { get; private set; }
    }

    public class LineInsertedEventArgs : EventArgs
    {
        public LineInsertedEventArgs(int index, int count)
        {
            this.Index = index;
            this.Count = count;
        }

        /// <summary>
        /// 	Inserted line index
        /// </summary>
        public int Index { get; private set; }

        /// <summary>
        /// 	Count of inserted lines
        /// </summary>
        public int Count { get; private set; }
    }

    public class LineRemovedEventArgs : EventArgs
    {
        public LineRemovedEventArgs(int index, int count, List<int> removedLineIds)
        {
            this.Index = index;
            this.Count = count;
            this.RemovedLineUniqueIds = removedLineIds;
        }

        /// <summary>
        /// 	Removed line index
        /// </summary>
        public int Index { get; private set; }

        /// <summary>
        /// 	Count of removed lines
        /// </summary>
        public int Count { get; private set; }

        /// <summary>
        /// 	UniqueIds of removed lines
        /// </summary>
        public List<int> RemovedLineUniqueIds { get; private set; }
    }

    /// <summary>
    /// 	TextChanged event argument
    /// </summary>
    public class TextChangedEventArgs : EventArgs
    {
        /// <summary>
        /// 	Constructor
        /// </summary>
        public TextChangedEventArgs(Range changedRange)
        {
            this.ChangedRange = changedRange;
        }

        /// <summary>
        /// 	This range contains changed area of text
        /// </summary>
        public Range ChangedRange { get; set; }
    }

    public class TextChangingEventArgs : EventArgs
    {
        public string InsertingText { get; set; }

        /// <summary>
        /// 	Set to true if you want to cancel text inserting
        /// </summary>
        public bool Cancel { get; set; }
    }

    public class WordWrapNeededEventArgs : EventArgs
    {
        public List<int> CutOffPositions { get; private set; }

        public bool ImeAllowed { get; private set; }

        public Line Line { get; private set; }

        public WordWrapNeededEventArgs(List<int> cutOffPositions, bool imeAllowed, Line line)
        {
            this.CutOffPositions = cutOffPositions;
            this.ImeAllowed = imeAllowed;
            this.Line = line;
        }
    }

    public enum WordWrapMode
    {
        /// <summary>
        /// 	Word wrapping by control width
        /// </summary>
        WordWrapControlWidth,

        /// <summary>
        /// 	Word wrapping by preferred line width (PreferredLineWidth)
        /// </summary>
        WordWrapPreferredWidth,

        /// <summary>
        /// 	Char wrapping by control width
        /// </summary>
        CharWrapControlWidth,

        /// <summary>
        /// 	Char wrapping by preferred line width (PreferredLineWidth)
        /// </summary>
        CharWrapPreferredWidth,

        /// <summary>
        /// 	Custom wrap (by event WordWrapNeeded)
        /// </summary>
        Custom
    }

    public class PrintDialogSettings
    {
        public PrintDialogSettings()
        {
            this.ShowPrintPreviewDialog = true;
            this.Title = "";
            this.Footer = "";
            this.Header = "";
        }

        public bool ShowPageSetupDialog { get; set; }

        public bool ShowPrintDialog { get; set; }

        public bool ShowPrintPreviewDialog { get; set; }

        /// <summary>
        /// 	Title of page. If you want to print Title on the page, insert code &amp;w in Footer or Header.
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// 	Footer of page.
        /// 	Here you can use special codes: &amp;w (Window title), &amp;D, &amp;d (Date), &amp;t(), &amp;4 (Time), &amp;p (Current page number), &amp;P (Total number of pages),  &amp;&amp; (A single ampersand), &amp;b (Right justify text, Center text. If &amp;b occurs once, then anything after the &amp;b is right justified. If &amp;b occurs twice, then anything between the two &amp;b is centered, and anything after the second &amp;b is right justified).
        /// 	More detailed see <see cref="http://msdn.microsoft.com/en-us/library/aa969429(v=vs.85).aspx">here</see>
        /// </summary>
        public string Footer { get; set; }

        /// <summary>
        /// 	Header of page
        /// 	Here you can use special codes: &amp;w (Window title), &amp;D, &amp;d (Date), &amp;t(), &amp;4 (Time), &amp;p (Current page number), &amp;P (Total number of pages),  &amp;&amp; (A single ampersand), &amp;b (Right justify text, Center text. If &amp;b occurs once, then anything after the &amp;b is right justified. If &amp;b occurs twice, then anything between the two &amp;b is centered, and anything after the second &amp;b is right justified).
        /// 	More detailed see <see cref="http://msdn.microsoft.com/en-us/library/aa969429(v=vs.85).aspx">here</see>
        /// </summary>
        public string Header { get; set; }

        /// <summary>
        /// 	Prints line numbers
        /// </summary>
        public bool IncludeLineNumbers { get; set; }
    }

    public class AutoIndentEventArgs : EventArgs
    {
        public AutoIndentEventArgs(
            int iLine, string lineText, string prevLineText, int tabLength, int currentIndentation)
        {
            this.iLine = iLine;
            this.LineText = lineText;
            this.PrevLineText = prevLineText;
            this.TabLength = tabLength;
            this.AbsoluteIndentation = currentIndentation;
        }

        public int iLine { get; internal set; }

        public int TabLength { get; internal set; }

        public string LineText { get; internal set; }

        public string PrevLineText { get; internal set; }

        /// <summary>
        /// 	Additional spaces count for this line, relative to previous line
        /// </summary>
        public int Shift { get; set; }

        /// <summary>
        /// 	Additional spaces count for next line, relative to previous line
        /// </summary>
        public int ShiftNextLines { get; set; }

        /// <summary>
        /// 	Absolute indentation of current line. You can change this property if you want to set absolute indentation.
        /// </summary>
        public int AbsoluteIndentation { get; set; }
    }

    /// <summary>
    /// 	Type of highlighting
    /// </summary>
    public enum HighlightingRangeType
    {
        /// <summary>
        /// 	Highlight only changed range of text. Highest performance.
        /// </summary>
        ChangedRange,

        /// <summary>
        /// 	Highlight visible range of text. Middle performance.
        /// </summary>
        VisibleRange,

        /// <summary>
        /// 	Highlight all (visible and invisible) text. Lowest performance.
        /// </summary>
        AllTextRange
    }

    /// <summary>
    /// 	Strategy of search of end of folding block
    /// </summary>
    public enum FindEndOfFoldingBlockStrategy
    {
        Strategy1,

        Strategy2
    }

    /// <summary>
    /// 	Strategy of search of brackets to highlighting
    /// </summary>
    public enum BracketsHighlightStrategy
    {
        Strategy1,

        Strategy2
    }

    /// <summary>
    /// 	ToolTipNeeded event args
    /// </summary>
    public class ToolTipNeededEventArgs : EventArgs
    {
        public ToolTipNeededEventArgs(Place place, string hoveredWord)
        {
            this.HoveredWord = hoveredWord;
            this.Place = place;
        }

        public Place Place { get; private set; }

        public string HoveredWord { get; private set; }

        public string ToolTipTitle { get; set; }

        public string ToolTipText { get; set; }

        public ToolTipIcon ToolTipIcon { get; set; }
    }

    /// <summary>
    /// 	HintClick event args
    /// </summary>
    public class HintClickEventArgs : EventArgs
    {
        public HintClickEventArgs(Hint hint)
        {
            this.Hint = hint;
        }

        public Hint Hint { get; private set; }
    }

    /// <summary>
    /// 	CustomAction event args
    /// </summary>
    public class CustomActionEventArgs : EventArgs
    {
        public FCTBAction Action { get; private set; }

        public CustomActionEventArgs(FCTBAction action)
        {
            this.Action = action;
        }
    }

    public enum TextAreaBorderType
    {
        None,

        Single,

        Shadow
    }

    [Flags]
    public enum ScrollDirection : ushort
    {
        None = 0,

        Left = 1,

        Right = 2,

        Up = 4,

        Down = 8
    }

#if Styles32
    /// <summary>
    /// Style index mask (32 styles)
    /// </summary>
    [Flags]
    public enum StyleIndex : uint
    {
        None = 0,
        Style0 = 0x1,
        Style1 = 0x2,
        Style2 = 0x4,
        Style3 = 0x8,
        Style4 = 0x10,
        Style5 = 0x20,
        Style6 = 0x40,
        Style7 = 0x80,
        Style8 = 0x100,
        Style9 = 0x200,
        Style10 = 0x400,
        Style11 = 0x800,
        Style12 = 0x1000,
        Style13 = 0x2000,
        Style14 = 0x4000,
        Style15 = 0x8000,

        Style16 = 0x10000,
        Style17 = 0x20000,
        Style18 = 0x40000,
        Style19 = 0x80000,
        Style20 = 0x100000,
        Style21 = 0x200000,
        Style22 = 0x400000,
        Style23 = 0x800000,
        Style24 = 0x1000000,
        Style25 = 0x2000000,
        Style26 = 0x4000000,
        Style27 = 0x8000000,
        Style28 = 0x10000000,
        Style29 = 0x20000000,
        Style30 = 0x40000000,
        Style31 = 0x80000000,

        All = 0xffffffff
    }
#else
    /// <summary>
    /// 	Style index mask (16 styles)
    /// </summary>
    [Flags]
    public enum StyleIndex : ushort
    {
        None = 0,

        Style0 = 0x1,

        Style1 = 0x2,

        Style2 = 0x4,

        Style3 = 0x8,

        Style4 = 0x10,

        Style5 = 0x20,

        Style6 = 0x40,

        Style7 = 0x80,

        Style8 = 0x100,

        Style9 = 0x200,

        Style10 = 0x400,

        Style11 = 0x800,

        Style12 = 0x1000,

        Style13 = 0x2000,

        Style14 = 0x4000,

        Style15 = 0x8000,

        All = 0xffff
    }
#endif
}